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

图像的二值化

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

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

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

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

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

cv4j 中,我们封装好了这些形态学的常用操作,比如开闭操作、腐蚀和膨胀等等。

其中,开操作的代码如下:

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的效果图


完整的demo效果.png

第三步如果看不太清楚,我们看一下放大的效果图


放大第三步的操作.png

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

准备工作展示原图

        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实现图片的滤镜效果

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容