二值图像分析:案例实战(文本分离+硬币计数)

图像的二值化

图像二值化就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果。
将256个亮度等级的灰度图像经过适当的阈值选取而得到仍然能够反映图像总体和局部特征的二值化图像。在数字图像处理中,二值图像占有很是重要的地位,首先,图像的二值化有利于图像的进一步处理,使图像变得简单,并且数据量减少,能凸显出感兴趣的目标的轮廓。其次,要进行二值图像的处理与分析,首先要把灰度图像二值化,获得二值化图像。java

在实际应用中,不少图像的分析最终都转换为二值图像的分析,好比:医学图像分析、前景检测、字符识别,形状识别。二值化+数学形态学能解决不少计算机识别工程中目标提取的问题。git

开操做演示---文本分离与切割

开操做是先腐蚀后膨胀的过程。用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。github

跟开操做相对应的是闭操做。另外,腐蚀和膨胀在下文中有介绍。算法

cv4j 中,咱们封装好了这些形态学的经常使用操做,好比开闭操做、腐蚀和膨胀等等。网络

其中,开操做的代码以下:dom

public class MorphOpen {
    /** * in order to remove litter noise block, erode + dilate operator * * @param binary * @param structureElement */
    public void process(ByteProcessor binary, Size structureElement) {
        Erode erode = new Erode();
        Dilate dilate = new Dilate();
        erode.process(binary, structureElement);
        dilate.process(binary, structureElement);
    }
}复制代码

先来看一个完整demo的效果图
post

完整的demo效果.png

第三步若是看不太清楚,咱们看一下放大的效果图
spa

放大第三步的操做.png

如上图所示,demo完成了文本的切割。咱们来看看具体的代码是怎么实现的。.net

准备工做展现原图3d

Resources res = getResources();
        final Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.test_binary1);
        image0.setImageBitmap(bitmap);复制代码

第一步二值化

CV4JImage cv4JImage = new CV4JImage(bitmap);
        Threshold threshold = new Threshold();
        threshold.process((ByteProcessor)(cv4JImage.convert2Gray().getProcessor()),Threshold.THRESH_TRIANGLE,Threshold.METHOD_THRESH_BINARY_INV,255);
        image1.setImageBitmap(cv4JImage.getProcessor().getImage().toBitmap());复制代码

第二步开操做

MorphOpen morphOpen = new MorphOpen();
cv4JImage.resetBitmap();
morphOpen.process((ByteProcessor)cv4JImage.getProcessor(),new Size(5));

image2.setImageBitmap(cv4JImage.getProcessor().getImage().toBitmap());复制代码

第三步连通组件标记

ConnectedAreaLabel connectedAreaLabel = new ConnectedAreaLabel();
        byte[] mask = new byte[cv4JImage.getProcessor().getWidth() * cv4JImage.getProcessor().getHeight()];
        List<Rect> rectangles = new ArrayList<>();
        connectedAreaLabel.process((ByteProcessor)cv4JImage.getProcessor(),mask,rectangles,true);
        cv4JImage.resetBitmap();
        Bitmap newBitmap = cv4JImage.getProcessor().getImage().toBitmap();

        if (Preconditions.isNotBlank(rectangles)) {
            Tools.drawRects(newBitmap,rectangles);
        }

        image3.setImageBitmap(newBitmap);复制代码

其实,作完第三步再结合ocr就能够识别出具体文字啦。若是再结合一下网络爬虫的话,意义更大。

虽然, cv4j 目前还只是移动端的库,可是它毕竟是java开发的,改为适合desktop的很容易。

腐蚀操做演示---硬币计数

腐蚀操做是一种消除边界点,使边界向内部收缩的过程。能够用来消除小且无心义的物体。腐蚀操做扫描图像的每个像素,用结构元素与其覆盖的二值图像作“与”操做:若是都为1,结果图像的该像素为1,不然为0。

跟腐蚀操做相对的是膨胀操做。腐蚀用于分割独立的图像元素,而膨胀用于链接相邻的元素。

腐蚀的算法:

腐蚀操做.png

其中,g(x,y)为腐蚀后的灰度图像,f(x,y)为原灰度图像,B为结构元素。腐蚀运算是由结构元素肯定的邻域块中选取图像值与结构元素值的差的最小值。

能够简化为:

简化的腐蚀操做.png

来看一个例子,原图中有不少硬币,经过一步步的分析计算出硬币的个数。

硬币计数1.png

硬币计数2.png

准备工做展现原图

Resources res = getResources();
        final Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.test_coins);
        image0.setImageBitmap(bitmap);复制代码

第一步二值化

CV4JImage cv4JImage = new CV4JImage(bitmap);
        Threshold threshold = new Threshold();
        threshold.process((ByteProcessor)(cv4JImage.convert2Gray().getProcessor()),Threshold.THRESH_OTSU,Threshold.METHOD_THRESH_BINARY_INV,255);
        image1.setImageBitmap(cv4JImage.getProcessor().getImage().toBitmap());复制代码

第二步腐蚀操做

Erode erode = new Erode();
        cv4JImage.resetBitmap();
        erode.process((ByteProcessor)cv4JImage.getProcessor(),new Size(3),10);
        image2.setImageBitmap(cv4JImage.getProcessor().getImage().toBitmap());复制代码

第三步连通组件标记

ConnectedAreaLabel connectedAreaLabel = new ConnectedAreaLabel();
        byte[] mask = new byte[cv4JImage.getProcessor().getWidth() * cv4JImage.getProcessor().getHeight()];

        int num = connectedAreaLabel.process((ByteProcessor)cv4JImage.getProcessor(),mask,null,false); // 获取连通组件的个数

        SparseIntArray colors = new SparseIntArray();
        Random random = new Random();

        int height = cv4JImage.getProcessor().getHeight();
        int width = cv4JImage.getProcessor().getWidth();
        int size = height * width;
        for (int i = 0;i<size;i++) {
            int c = mask[i] & 0xff;
            colors.put(c,Color.argb(255, random.nextInt(255),random.nextInt(255),random.nextInt(255)));
        }

        cv4JImage.resetBitmap();
        Bitmap newBitmap = cv4JImage.getProcessor().getImage().toBitmap();

        for(int row=0; row<height; row++) {
            for (int col = 0; col < width; col++) {

                int c = mask[row*width+col] & 0xff;
                if (c>0) {
                    newBitmap.setPixel(col,row,colors.get(c));
                }
            }
        }

        image3.setImageBitmap(newBitmap);

        if (num>0)
            numTextView.setText(String.format("总计识别出%d个硬币",num));复制代码

最终获取了连通组件的个数也就是硬币的个数,而且在已经识别的硬币上随机着色。

#总结
cv4jgloomyfish和我一块儿开发的图像处理库,纯java实现,目前还处于早期的版本。这周,咱们开始作二值图像的分析(腐蚀、膨胀、开闭操做、轮廓提取等等),这个模块并无完成所有功能,预计下周能完工。

先前的文章:
Java实现高斯模糊和图像的空间卷积
Java实现图片滤镜的高级玩法
Java实现图片的滤镜效果

相关文章
相关标签/搜索