• 有趣的opencv-记录图片二值化和相似度实现


    一、背景:

    前面的文章提到,要实现两个功能

    1. 实现数字识别

    2. 实现图标识别

    前面的文章已经已经实现了数字的识别,但是发现识别率比较低,并且识别的错误率也比较高。考虑是因为背景比较复杂影响了识别效果,本文主要解决复杂背景的简化,以及图片的对比

    二、目标:

    1. 通过opencv对图片处理,使背景和内容有更加明显的差异,即二值化

    2. 通过opencv对比二值化后的图片的相似度,来识别图标

    三、实现过程:

    opencv的集成

    1. 下载opencv对应的版本 https://opencv.org/releases/

    我使用了最新的4.6.0 的版本

    2. 解压下载的zip得到demo和需要集成的module

    3. 接入自己的项目

    3.1、将2中得到的sdk作为一个独立module放到项目中

    3.2、在项目的setting.gradle中引入sdk module

    3.3、 在使用opencv的module中引入sdk module

    4. 编译使用

    至此集成算是完成了,但是因为这个使用了ndk相关的功能,如果本地没有ndk环境的话,可能需要处理下环境问题,这里不再赘述

    二值化处理

    1. 先上代码

    1.     public static Bitmap createBitmap(Bitmap bitmap) {
    2.         Mat src = new Mat();
    3.         Utils.bitmapToMat(bitmap, src); //将bitmap转换为Mat
    4.         Mat thresholdImage = new Mat(src.size(), src.type()); //这个二值图像用于找出关键信息的图像
    5.   
    6.         //将图像转换为灰度图像
    7.         Imgproc.cvtColor(src, thresholdImage, Imgproc.COLOR_RGBA2GRAY);
    8.         //将图像转换为边缘二值图像
    9.         Imgproc.threshold(thresholdImage,thresholdImage,10.0,255.0, Imgproc.THRESH_BINARY_INV|Imgproc.THRESH_OTSU);
    10.         Bitmap binaryBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
    11.         Utils.matToBitmap(thresholdImage, binaryBitmap);
    12.         return binaryBitmap;
    13.     }

    2. 步骤拆解

    2.1、先将bitmap转为 Mat方便opencv进行操作

    2.2、 调用 Imgproc.cvtColor(src, thresholdImage, Imgproc.COLOR_RGBA2GRAY); 将图像转为灰度图像

    2.3、 调用 ` Imgproc.threshold(thresholdImage,thresholdImage,10.0,255.0, Imgproc.THRESH_BINARY_INV|Imgproc.THRESH_OTSU);

    ` 将图像转为二值图像。 这个方法非常的重要,在这里专门说一下

    1. 第一个参数是图像来源

    2. 第二个参数是输出的图像

    3. 是一个标准。每个像素点会和这个标准比较。这个会和最后一个参数关联在一起使用。

    4. 在符合第三个参数的标准的情况下,要赋予的值

    5. 第五参数控制第三、第四个参数的使用情况

    3. 效果展示

    相似度处理

    1. 上代码

    1.      public static Double similarity(Bitmap bitmap1, Bitmap bitmap2){
    2.             // 计算每张图片的特征点
    3.             MatOfKeyPoint descriptors1 = computeDescriptors(bitmap1);
    4.             MatOfKeyPoint descriptors2 = computeDescriptors(bitmap2);
    5.             // 比较两张图片的特征点
    6.             DescriptorMatcher descriptorMatcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
    7.             List matches =new  ArrayList();
    8.             // 计算大图中包含多少小图的特征点。
    9.             // 如果计算小图中包含多少大图的特征点,结果会不准确。
    10.             // 比如:若小图中的 50 个点都包含在大图中的 100 个特征点中,则计算出的相似度为 100%,显然不符合我们的预期
    11.             if (bitmap1.getByteCount() > bitmap2.getByteCount() ) {
    12.                 descriptorMatcher.knnMatch(descriptors1, descriptors2, matches, 2);
    13.             } else {
    14.                 descriptorMatcher.knnMatch(descriptors2, descriptors1, matches, 2);
    15.             }
    16.             Log.i("~~~""matches.size: ${matches.size}");
    17.             if (matches.isEmpty()) return 0.00;
    18.             // 获取匹配的特征点数量
    19.             int matchCount = 0;
    20.             // 邻近距离阀值,这里设置为 0.7,该值可自行调整
    21.             float nndrRatio = 0.7f;
    22.             for (MatOfDMatch match:matches) {
    23.                 DMatch[] array = match.toArray();
    24.                 // 用邻近距离比值法(NNDR)计算匹配点数
    25.                 if (array[0].distance <= array[1].distance * nndrRatio) {
    26.                     matchCount++;
    27.                 }
    28.             }
    29.             Log.i("~~~""matchCount: $matchCount");
    30.             return Double.valueOf(matchCount/ matches.size());
    31.         }

    2. 步骤拆解

    2.1、 找到两个图片的特征点

    1.    private static MatOfKeyPoint computeDescriptors(Bitmap bitmap){
    2.             Mat mat = new Mat();
    3.             Utils.bitmapToMat(bitmap, mat);
    4.             MatOfKeyPoint keyPoints = new MatOfKeyPoint();
    5.             siftDetector.detect(mat, keyPoints);
    6.             MatOfKeyPoint descriptors = new MatOfKeyPoint();
    7.             // 计算图片的特征点
    8.             siftDetector.compute(mat, keyPoints, descriptors);
    9.             return descriptors;
    10.         }

    2.2、 选取合适的匹配模式

    2.3、避免 大图中包含小图导致认为小图完全匹配大图的场景,所以对大小进行判断,用大图和小图比。

    2.4、 选取一个邻近距离阈值

    2.5、 比较两个图的特征值的差异,,将差值符合阈值的个数记录

    2.6、 符合阈值的个数,比上总特征数,得到匹配率

    总结

    opencv提供了一整套非常完善的api,可以解决我们遇到的绝大部分场景,大家可以多看文档,学习起来。

    关注公众号: arigeweixin ,取得更多联系

  • 相关阅读:
    2024年天津农学院专升本增加水文与水资源专业更名报考范围的通知
    Pooling Revisited: Your Receptive Field is Suboptimal 论文解读和感想
    Unity | 以附加模式加载场景,实现多场景叠加及注意事项
    通过onnxruntime进行模型部署过程中的问题
    视频截取gif动画怎么操作?轻松一键快速视频转gif
    【TCP:可靠数据传输,快速重传,流量控制,TCP流量控制】
    云边端协同场景下的“AI+”视频融合能力,如何赋能多行业应用?
    【Vue】npm install 命令
    SSH 基础学习使用
    安卓手机可成为天气预报工具?这项全球科学项目有意思
  • 原文地址:https://blog.csdn.net/Lucifer_art/article/details/126122143