好好学习,天天向上

本文已收录至我的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实现二值点阵图识别的更多相关文章

  1. python图片二值化提高识别率

    import cv2from PIL import Imagefrom pytesseract import pytesseractfrom PIL import ImageEnhanceimport ...

  2. Java 对二值化图片识别连通域

    用Java 对 已经 二值化了的图片 标记连通域 每块的连通域都标记不一样的数字 public static void main(String [] args) throws IOException ...

  3. 自己定义View之Chart图标系列(1)——点阵图

    近期要做一些图表类的需求,一開始就去github上看了看,发现开源的图表框架还是蛮多的.可是非常少有全然符合我的需求的.另外就是使用起来比較麻烦.所以就决定自己来造轮子了~~~ 今天要介绍的就是And ...

  4. python实现图像二值化

    1.什么是图像二值化 彩色图像: 有blue,green,red三个通道,取值范围均为0-255 灰度图:只有一个通道0-255,所以一共有256种颜色 二值图像:只有两种颜色,黑色和白色,二值化就是 ...

  5. 最牛逼android上的图表库MpChart(二) 折线图

    最牛逼android上的图表库MpChart二 折线图 MpChart折线图介绍 MpChart折线图实例 MpChart效果 最牛逼android上的图表库MpChart(二) 折线图 最近工作中, ...

  6. 玩转Android之二维码生成与识别

    二维码,我们也称作QRCode,QR表示quick response即快速响应,在很多App中我们都能见到二维码的身影,最常见的莫过于微信了.那么今天我们就来看看怎么样在我们自己的App中集成二维码的 ...

  7. 机器学习进阶-案例实战-答题卡识别判 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 ...

  8. 机器学习进阶-项目实战-信用卡数字识别 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表示需要填写的 ...

  9. atitit.验证码识别step4--------图形二值化 灰度化

    atitit.验证码识别step4--------图形二值化 灰度化 1. 常见二值化的方法原理总结 1 1.1. 方法一:该方法非常简单,对RGB彩色图像灰度化以后,扫描图像的每个像素值,值小于12 ...

随机推荐

  1. 几种定时任务(Timer、TimerTask、ScheduledFuture)的退出—结合真实案例【JAVA】

    工作中常常会有定时任务的开发需求,特别是移动端.最近笔者正好有所涉及,鉴于此,结合开发中的案例说明一下几种定时任务的退出. 需求说明:定时更新正在生成的文件大小和状态[进行中.失败.完成],如果文件生 ...

  2. MIT 6.828 Lab 1/ Part 2

    Exercise 03 - obj/boot/boot.asm 反汇编文件 截取asm部分文件并注释理解 # Set up the important data segment registers ( ...

  3. 逃离CSDN -慕舲的黑夜-第三期

    来时,是朋友推荐查资料,后来看到CSDN的UI,好华丽高大上,也读了CSDN首页推荐的一些文章,加入CSDN. 可是后来随着博客园,蓝奏云,w3c菜鸟教程,等平台的出现,CSDN越来越令人心寒

  4. javaWeb项目之图书管理系统(附视频讲解)

    视频播放地址:javaWeb图书系统 本系统为"Swing项目之图书管理系统"(此源码已共享)的Web版,网页框架用采用EasyUI 数据库为MysqL,写Web项目摒弃了火狐浏览 ...

  5. Keras结合Keras后端搭建个性化神经网络模型(不用原生Tensorflow)

    Keras是基于Tensorflow等底层张量处理库的高级API库.它帮我们实现了一系列经典的神经网络层(全连接层.卷积层.循环层等),以及简洁的迭代模型的接口,让我们能在模型层面写代码,从而不用仔细 ...

  6. latex:公式的序号

    1.排序单位 在文类book或report中,行间公式是以章为排序单位的,即每一新章节开始,公式序号计数器equation就被清零.比如第1章第3个公式的序号是(1.3),第2章第1个公式的序号是(2 ...

  7. SpringBoot系列——MyBatis-Plus整合封装

    前言 MyBatis-Plus是一款MyBatis的增强工具(简称MP),为简化开发.提高效率,但我们并没有直接使用MP的CRUD接口,而是在原来的基础上封装一层通用代码,单表继承我们的通用代码,实现 ...

  8. Node.js小项目——学生信息管理系统

    这是迄今为止第一次接触后端的东西,是一个很小的项目,但是对于前端学习入门很好.我是先学了VUE框架再学的Node,学起来比较轻松,不过每个人都有自己的学习方法️ 一.项目描述 学生信息管理系统,可以实 ...

  9. .net core 3.0 web api 重点设置,主要为了解决axios post不到参数问题

    这两天研究.net core 3.0升级,前端vue+axios 后端web api.测试过程中发现post的时候,由于提交的是json对象,后端web api获取不到数据. 今天贴了下解决过程.主要 ...

  10. Qt 如何使窗体初始最大化

    Qt 如何使窗体初始最大化 使用以下函数即可解决: void QWidget::setWindowState ( Qt::WindowStateswindowState ) 这样的函数,通过它可以设置 ...