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 ...
随机推荐
- IOS 单例崩溃分析 2014-12-10 15:46:36
单例模式是常用的模式,但是在单例应用中偶或引发崩溃让人匪夷所思.其实真的是单例引起的吗?未必.但是现象都指向了是单例引起的.今天我亲身经历了看似崩溃在单例上的一个例子,但实则不是,今天做个记录用于今后 ...
- Java引用类型之软引用(1)
Java使用SoftReference来表示软引用,软引用是用来描述一些“还有用但是非必须”的对象.对于软引用关联着的对象,在JVM应用即将发生内存溢出异常之前,将会把这些软引用关联的对象列进去回收对 ...
- Fisher Coffee 测评(非严格控温控水)
Fisher Coffee 测评(非严格控温控水) 咖啡生产批次:2020-05-29 打分区间:1~5,0.5间隔 批次:2020.6.3 酸为主,苦为主. 无甘,有甘,微甘,较甜,甘甜. 不苦,有 ...
- 团队作业1——团队展示&选题(银河超级无敌舰队)
一.团队展示 1.队名: 银河超级无敌舰队 2.队员学号: 姓名 学号 郭奕材(组长) 3118004959 刘婉儿(PM) 3218004994 辜仰淦 3118004957 王煜墉 3118004 ...
- 强化学习中的经验回放(The Experience Replay in Reinforcement Learning)
一.Play it again: reactivation of waking experience and memory(Trends in Neurosciences 2010) SWR发放模式不 ...
- Statistics and Samples in Distributional Reinforcement Learning
郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! arXiv:1902.08102v1 [stat.ML] 21 Feb 2019 Abstract 我们通过递归估计回报分布的统计量,提供 ...
- HM16.0之帧内模式——xCheckRDCostIntra()函数
参考:https://blog.csdn.net/nb_vol_1/article/category/6179825/1? 1.源代码: Void TEncCu::xCheckRDCostIntra( ...
- mysql高级内容学习总结
创建索引 create [unique] index indexname on tablename(columnname(length)) alter tablename add [unique] i ...
- mysql事务级别和spring中应用
一.事务的基本要素(ACID) 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节.事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有 ...
- DNSPod 修改NS 服务器?
其实我几乎没在国内注册过域名,更没想过用国内的DNS 服务,DNSPod 也是属于听说过名字的地步而已,但是正好在腾讯云注册了一个cn 域名,又觉得对DNSPod 的DNS 服务不是特别满意,所以想把 ...