Android实现二值点阵图识别
好好学习,天天向上
本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star
前言
我这几天在做一个东西,就是一张像二维码这样的 n*n 的只有两种颜色的点阵图,识别出哪个方块是深色的,哪个方块是浅色的。就像下面这张图
我一开始想的是,既然是图像识别,那不是OpenCV嘛。但是我不会呀,所以就开始研究,发现Android要使用OpenCV还涉及到JNI,NDK。算了算了好复杂,还是直接在网上找现成的轮子吧。找了一圈后,发现没有找到...既然没有轮子,那就自己造
然后想来想去,发现被自己蠢哭了,这个貌似不需要用到OpenCV吧,是我把问题想复杂了。然后开始第二种方案:比如这张图是 24*24 的,那我就把这样图缩小到24px * 24px,那不就正好一个像素对应一个格子嘛,哈哈哈。但最后还是淘汰了这个方案,因为我最终要实现的是拍照再裁切成肉眼的1:1的照片进行识别,因为肉眼裁切或者拍照的时候手机歪了一点点,很难做到正好1:1。这样的话压缩到一个格子对应一个像素这样高的精度很大可能性会造成误差。
然后方案就进化到了第三代,不去进行压缩了,直接计算出每个格子中心像素的坐标,然后判断色值就可以得到这个格子是什么颜色的了。因为不压缩的话每个格子就会占很多个像素,就算图片不是正好的1:1,也不太会出现误差。
正文
废话了一大堆,介绍完方案就来说一下具体实现吧。首先需要明确一个问题:如何去计算某个格子的中心像素的位置,用一个公式就可以得出:
(2 * n+1) * length /(2 * num)
简单解释一下:当知道一条边的长度和格子数后就可以计算出每个格子占多少个像素 length/num,求某一个格子的中心坐标,比如第10个格子,因为从0开始的。所以就是 (2*(10-1)+1) 个半个格子的长度。
先灰度图片,目的是减少误差,然后将Bitmap图片用一个二维数组表示,二维数组的每个元素的值就是对应图片中点的色值。然后通过计算每个方格中心点的坐标,将值从二维数组中取出,放入存放每个方格中心点色值的数组newPx中。最后再遍历一遍newPx,当大于某个值就说明是该方块是白色的,小于某个值则说明该方块是黑色的。那么这个某个值是多少呢,就是所有方格中心像素色值最大值与最小值的平均值。
原理介绍完了,完整代码如下:
/**
* 展示图片
* @param imagePath 图片的路径
*/
private void displayImage(String imagePath) {
if (imagePath != null) {
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
Bitmap greyBitmap = convertGreyImg(bitmap);
String pixels = getBitString(greyBitmap, gridNum);
picturePixels.setText(pixels);
} else {
Toast.makeText(this, "failed to get image", Toast.LENGTH_SHORT).show();
}
}
/**
* 获取转化后的表示方格颜色的字符串,黑色用 ● 表示,白色用 ○ 表示
* @param img
* @param num 方格数,总方格数为num * num
* @return
*/
private String getBitString(Bitmap img, int num) {
int width = img.getWidth();//原图像宽度
int height = img.getHeight();//原图像高度
int[] oldPx = new int[width * height];//用来存储原图每个像素点的颜色信息
int[] newPx = new int[num * num]; //用来存放每个方格中心点像素颜色值
int index = 0;
img.getPixels(oldPx, 0, width, 0, 0, width, height);//获取原图中的像素信息
int[][] oldPxTwo = twoArray(oldPx,width,height); //原图每个像素点颜色信息的二维数组
int minValue = Integer.MAX_VALUE;
int maxValue = Integer.MIN_VALUE;
//循环
for (int i = 0; i < num; i++) {
int row = getCoordinate(i,width,num);
for (int j = 0; j < num; j++) {
int col = getCoordinate(j,height,num);
minValue = Math.min(minValue,oldPxTwo[row][col]);
maxValue = Math.max(maxValue,oldPxTwo[row][col]);
newPx[index++] = oldPxTwo[row][col];
}
}
StringBuilder pixels = new StringBuilder();
int middleValue = (minValue + maxValue)/2;
for (int i = 0; i < newPx.length; i++) {
if (i>0 && i%num==0) pixels.append("\n");
if (newPx[i] > middleValue) {
pixels.append("○");
} else {
pixels.append("●");
}
}
return pixels.toString();
}
/**
* 获取方块中心点 横/纵 坐标
* @param n 第几个方块,从0开始
* @param length 横向或者纵向的长度
* @param num 横向或者纵向方块数
* @return 坐标值
*/
private int getCoordinate(int n,int length,int num) {
return (2*n+1)*length /(2*num);
}
/**
* 一维数组转化为二维数组
* @param arr
* @param width 纵向的方块数,多少行
* @param height
* @return
*/
public int[][] twoArray(int[] arr,int width,int height) {
int[][] result = new int[height][width];
int k = 0;
for (int i = 0;i<height;i++) {
for (int j = 0;j<width;j++) {
result[i][j] = arr[k++];
}
}
return result;
}
/**
* 将彩色图转换为灰度图
* @param img 位图
* @return 返回转换好的位图
*/
public Bitmap convertGreyImg(Bitmap img) {
int width = img.getWidth(); //获取位图的宽
int height = img.getHeight(); //获取位图的高
int[] pixels = new int[width * height]; //通过位图的大小创建像素点数组
img.getPixels(pixels, 0, width, 0, 0, width, height);
int alpha = 0xFF << 24;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int grey = pixels[width * i + j];
int red = ((grey & 0x00FF0000) >> 16);
int green = ((grey & 0x0000FF00) >> 8);
int blue = (grey & 0x000000FF);
grey = (int) ((float) red * 0.3 + (float) green * 0.59 + (float) blue * 0.11);
grey = alpha | (grey << 16) | (grey << 8) | grey;
pixels[width * i + j] = grey;
}
}
Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
result.setPixels(pixels, 0, width, 0, 0, width, height);
return result;
}
这个转化为灰度图的方法是我在网上找的。
展示一下效果:
总结
好了,到这里就介绍完了,听我介绍完是不是觉得挺简单的。因为本篇文章主要是讲如何讲如何识别点阵图,所以调用相册等内容就没有说,其实我这个Demo调用相册的代码也是直接从《第一行代码》中拿来用的,不是自己写的。代码还可以再优化一下的,比如一位数组转二维数组可以和灰度图片一起实现,不过这些就留给小伙伴们自己去实现啦!
都看完了,来个 "
赞
" "赞
" "赞
" 鼓励一下我呗...本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star
Android实现二值点阵图识别的更多相关文章
- python图片二值化提高识别率
import cv2from PIL import Imagefrom pytesseract import pytesseractfrom PIL import ImageEnhanceimport ...
- Java 对二值化图片识别连通域
用Java 对 已经 二值化了的图片 标记连通域 每块的连通域都标记不一样的数字 public static void main(String [] args) throws IOException ...
- 自己定义View之Chart图标系列(1)——点阵图
近期要做一些图表类的需求,一開始就去github上看了看,发现开源的图表框架还是蛮多的.可是非常少有全然符合我的需求的.另外就是使用起来比較麻烦.所以就决定自己来造轮子了~~~ 今天要介绍的就是And ...
- python实现图像二值化
1.什么是图像二值化 彩色图像: 有blue,green,red三个通道,取值范围均为0-255 灰度图:只有一个通道0-255,所以一共有256种颜色 二值图像:只有两种颜色,黑色和白色,二值化就是 ...
- 最牛逼android上的图表库MpChart(二) 折线图
最牛逼android上的图表库MpChart二 折线图 MpChart折线图介绍 MpChart折线图实例 MpChart效果 最牛逼android上的图表库MpChart(二) 折线图 最近工作中, ...
- 玩转Android之二维码生成与识别
二维码,我们也称作QRCode,QR表示quick response即快速响应,在很多App中我们都能见到二维码的身影,最常见的莫过于微信了.那么今天我们就来看看怎么样在我们自己的App中集成二维码的 ...
- 机器学习进阶-案例实战-答题卡识别判 1.cv2.getPerspectiveTransform(获得投射变化后的H矩阵) 2.cv2.warpPerspective(H获得变化后的图像) 3.cv2.approxPolyDP(近似轮廓) 4.cv2.threshold(二值变化) 7.cv2.countNonezeros(非零像素点个数)6.cv2.bitwise_and(与判断)
1.H = cv2.getPerspectiveTransform(rect, transform_axes) 获得投射变化后的H矩阵 参数说明:rect表示原始的位置左上,右上,右下,左下, tra ...
- 机器学习进阶-项目实战-信用卡数字识别 1.cv2.findContour(找出轮廓) 2.cv2.boudingRect(轮廓外接矩阵位置) 3.cv2.threshold(图片二值化操作) 4.cv2.MORPH_TOPHAT(礼帽运算突出线条) 5.cv2.MORPH_CLOSE(闭运算图片内部膨胀) 6. cv2.resize(改变图像大小) 7.cv2.putText(在图片上放上文本)
7. cv2.putText(img, text, loc, text_font, font_scale, color, linestick) # 参数说明:img表示输入图片,text表示需要填写的 ...
- atitit.验证码识别step4--------图形二值化 灰度化
atitit.验证码识别step4--------图形二值化 灰度化 1. 常见二值化的方法原理总结 1 1.1. 方法一:该方法非常简单,对RGB彩色图像灰度化以后,扫描图像的每个像素值,值小于12 ...
随机推荐
- Python+Pytest+Allure+Git+Jenkins接口自动化框架
Python+Pytest+Allure+Git+Jenkins接口自动化框架 一.接口基础 接口测试是对系统和组件之间的接口进行测试,主要是效验数据的交换,传递和控制管理过程,以及相互逻辑依赖关系. ...
- ubuntu开发机所需工具,做个记录,不断补充
文件搜索 FSearch 用了下可以, 类似windows下的Everything 或者mac的cmd+空格 地址 安装: sudo add-apt-repository ppa:christian- ...
- 【java】java获取JVM启动参数 System.getProperty
java获取JVM启动参数 System.getProperty取 -D后的key即可 public class Test { public static void main(String[] arg ...
- Typescript node starter 3. App Router Controller
Request request对象表示HTTP请求,并具有请求query字符串.参数.body.HTTP headers等的属性.除了添加新的属性和方法外,还包含原型的属性和方法. 随着系列文章的发布 ...
- SparkStreaming概述
Spark Streaming 是Spark核心API的一个扩展,可以实现高吞吐量的.具备容错机制的实时流数据的处理. ◆ 支持从多种数据源获取数据,包括Kafka.Flume.Twitter.Zer ...
- 【接口自动化】Python+Requests接口自动化测试框架搭建【二】
接续前文,在上篇博客中我们编写了demo.py代码,里面代码过多冗余,更新代码: #!/usr/bin/env python # coding=utf-8 import requests class ...
- IdentityServer4网页(单点)登陆入门
前言 本篇说说ids中的网页登陆以及单点登陆的大致原理,主要是以基本跑通为目的,下一篇开始会详细说明集成ids网页登陆原理. 最好先熟悉以下知识: asp.net core asp.net core的 ...
- Revisiting Fundamentals of Experience Replay
郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! ICML 2020 Abstract 经验回放对于深度RL中的异策算法至关重要,但是在我们的理解上仍然存在很大差距.因此,我们对Q学习方法 ...
- Hive 常见面试题(二)
1.Hive行转列和列转行如何实现? 行转列 使用 concat_ws 实现行转列. 例如: select user_id, concat_ws(',',collect_list(order_id)) ...
- 从零开始的SpringBoot项目 ( 二 ) 使用IDEA创建一个SpringBoot项目
工欲善其事 , 必先利其器 . IntelliJ IDEA 2019.3.3 x64的安装与破解 下面详细说明下如何使用idea创建我们的第一个springboot项目: 首先打开idea主界面选择 ...