OPENCV清晰度判断(二)

文章目录

之前有过一篇关于清晰度的判断的文章: python的opencv操作记录(九)------图像清晰度计算

这一篇里面主要说的是用sobel算子,或者是拉普拉斯算子来进行梯度的计算,根据清晰的图像梯度更大的逻辑来判断图像的清晰度。

不过这种算法有一个问题,就是整个图像是有足够的信息来提供梯度,或者换句话说,图像大部分区域都是有效区域。

在实际项目中,碰到一种拍摄细胞的情况,需要对细胞对焦过程中的图像进行清晰度判断:

对于这种情况,直接计算图像的梯度,是不太有效果的,因为整个图像大部分区域的梯度都是一样的(模糊的,清晰的,只有小部分区域会有一些梯度差别),所以直接计算整图的梯度方式是无效的。

所以必须用别的办法。

提取ROI

既然不能直接对整张图像进行计算,那么首先就是提取ROI:

基本逻辑:

  • 转换灰度图
  • 阈值化,我这里用的是THRESH_BINARY_INV
  • 找轮廓
  • 求轮廓的外接RECT

当然,在具体的过程中,还可以加一些形态学的方法,比如膨胀,腐蚀之类。

cv::Mat getMasks(cv::Mat img)
{
    cv::Mat kernel, gray; 
    vector<cv::Mat> contours;

    if (img.channels() == 3)
    {
        cv::cvtColor(img, gray, cv::COLOR_RGB2GRAY);
    }

    cv::threshold(gray, gray, 100, 255, cv::THRESH_BINARY_INV);

    cv::findContours(gray, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);

    cv::Rect bestRect;
    int maxRectArea = -1;

    if (contours.size() == 0)
    {
        img = cv::Mat::zeros(cv::Size(1, 1), CV_8UC1);

        return img;
    }

    for(int i = 0; i < contours.size(); i++)
    {
        cv::Scalar color(0, 255, 0);
        cv::Rect rect = cv::boundingRect(contours[i]);

        if (rect.width * rect.height > maxRectArea)
        {
            maxRectArea = rect.width * rect.height;
            bestRect = rect;
            //cv::rectangle(img, rect, color, 1);
        }
    }

    cv::Mat roi(img, bestRect);

    return roi;
}

判断清晰度

提取出有效区域之后,就开始尝试用各种办法来进行判断了。最关键的是下面两张:

这两张图,从人眼来分辨的话,是后者比较清晰。但是如果直接使用求梯度的方案的话,前面一张的梯度会比较高,因为外面有一圈光圈一样的东西,造成了梯度反而比较高。

灰度共轭矩阵(GLCM)

首先尝试了一些提取特征的方案,第一个就是灰度共轭矩阵的计算:

灰度共轭函数的简单原理:


  • 几个关键概念,根据相邻像素点之间距离参数D不同可以得到不同距离的GLCM。此外对正常的灰度图像来说,最小灰度值为0,最大的灰度值为255,共计256个灰度级别,所以GLCM的大小为256x256,但是我们可以对灰度级别进行降维操作,比如可以每8个灰度值表示一个level这样,这样原来256x256大小的共生矩阵就可以改成256/8 * 256 /8 = 32x32的共生矩阵。所以最终影响灰度共生矩阵生成有三个关键参数:
    • 角度 (支持0、45、90、135)
    • 距离(大于等于1个像素单位)
    • 灰度级别(最大GLCM=256 x 56)

计算灰度共轭矩阵代码

cv::Mat getGlcm_0(cv::Mat img)
{
    if (img.channels() == 3)
    {
        cv::cvtColor(img, img, cv::COLOR_RGB2GRAY);
    }

    // 像素距离d=1
    int d = 1;
    int step = 256 / d;

    cv::Mat glcm = cv::Mat::zeros(cv::Size(step, step), CV_8UC1);

    for (int row = 0; row < img.rows; row++) {
        for (int col = 0; col < img.cols - 1; col++) {
            int i = img.at<uchar>(row, col);
            int j = img.at<uchar>(row, col + 1);
            glcm.at<uchar>(i, j)++;
        }
    }

    return glcm;
}

如果是其他角度的话,把for循环的部分修改一下即可,就不全放上来了。

计算矩阵的对比度

float contrast_need = 0;
for (int i = 0; i < 256; i++) {
    for (int j = 0; j < 256; j++) {
        contrast_need += glcm.at<uchar>(i, j) * (i - j) * (i - j);
    }
}

直接将矩阵的各个元素的差值进行计算,但是对上述两张图没有起到作用,还是前者的计算值更高。

只能再换别的。

LBP:

使用提取纹理的另外一个方法:LBP。

LBP的基本原理

  • 对图像中的所有点,以该点为中心,取3x3的邻域窗口;
  • 将8-邻域像素值与中心点像素值进行比较,大于或等于中心像素标记为1,否则标记为0;
  • 将周围0-1序列,以一定的顺序排列,成一个8位的无符号的二进制数,转化成整数;
  • 这个整数就是表征这个窗口的LBP值。

LBP值共有2的8次方种可能,因此LBP值有256种。中心像素的LBP值反映了该像素周围区域的纹理信息。 以上,便是最基本的LBP算子。由于直接利用的灰度比较,所以其具有灰度不变性;

LBP代码

cv::Mat LBP(cv::Mat img)
{
    if (img.channels() == 3)
    {
        cv::cvtColor(img, img, cv::COLOR_RGB2GRAY);
    }

    cv::Mat result;
    result.create(img.rows - 2, img.cols - 2, img.type());

    result.setTo(0);

    int totalCode = 0;
    for (int i = 1; i < img.rows - 1; i++)
    {
        for (int j = 1; j < img.cols - 1; j++)
        {
            uchar center = img.at<uchar>(i, j);
            uchar code = 0;
            code |= (img.at<uchar>(i - 1, j - 1) >= center) << 7;
            code |= (img.at<uchar>(i - 1, j) >= center) << 6;
            code |= (img.at<uchar>(i - 1, j + 1) >= center) << 5;
            code |= (img.at<uchar>(i, j + 1) >= center) << 4;
            code |= (img.at<uchar>(i + 1, j + 1) >= center) << 3;
            code |= (img.at<uchar>(i + 1, j) >= center) << 2;
            code |= (img.at<uchar>(i + 1, j - 1) >= center) << 1;
            code |= (img.at<uchar>(i, j - 1) >= center) << 0;
            result.at<uchar>(i - 1, j - 1) = code;
            totalCode += code;
        }
    }

    cout << totalCode << endl;
    return result;
}

很郁闷的,对于上述两张图,还是没什么太多作用。

相关推荐
写代码的中青年14 分钟前
Semantic Kernel:微软大模型开发框架——LangChain 替代
人工智能·python·microsoft·langchain·大模型·llm
悸尢17 分钟前
二维舵机颜色追踪,使用树莓派+opencv+usb摄像头+两个舵机实现颜色追踪,采用pid调控
人工智能·opencv·计算机视觉
深圳市青牛科技实业有限公司 小芋圆19 分钟前
什么是空气电容器?
人工智能·单片机·嵌入式硬件·语音识别·小家电
神经蛙199625 分钟前
「豆包Marscode体验官」- 智能编程的新纪元
人工智能
*wj30 分钟前
【详解】RV1106移植opencv-mobile库
人工智能·opencv·计算机视觉
Baihai IDP41 分钟前
Llama-2 vs. Llama-3:利用微型基准测试(井字游戏)评估大模型
人工智能·ai·llm·llama·白海科技·大模型评估
汀、人工智能43 分钟前
AI Agent框架(LLM Agent):LLM驱动的智能体如何引领行业变革,应用探索与未来展望
人工智能·大模型·agent
记录&日常1 小时前
【已解决】Anaconda中conda 某个包之后Solving environment: \一直转 卡住不动解决办法(图文教程)
人工智能
CodeArtisanX1 小时前
高效管理 TensorFlow 2 GPU 显存的实用指南
人工智能·python·tensorflow
图灵追慕者1 小时前
深度学习之OpenCV的DNN模块
深度学习·opencv·dnn