OPENCV清晰度判断(二)

文章目录

  • 提取ROI
  • 判断清晰度
    • 灰度共轭矩阵(GLCM)
      • 灰度共轭函数的简单原理:
      • 计算灰度共轭矩阵代码
      • 计算矩阵的对比度
    • LBP:
      • LBP的基本原理
      • LBP代码

之前有过一篇关于清晰度的判断的文章: 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;
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/754335.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

代理IP对SEO影响分析:提升网站排名的关键策略

你是否曾经为网站排名难以提升而苦恼&#xff1f;代理服务器或许就是你忽略的关键因素。在竞争激烈的互联网环境中&#xff0c;了解代理服务器对SEO的影响&#xff0c;有助于你采取更有效的策略&#xff0c;提高网站的搜索引擎排名。本文将为你详细分析代理服务器在SEO优化中的…

使用FRP 0.58版本进行内网穿透的详细教程

什么是FRP&#xff1f; FRP&#xff08;Fast Reverse Proxy&#xff09;是一款高性能的反向代理应用&#xff0c;主要用于内网穿透。通过FRP&#xff0c;您可以将内网服务暴露给外网用户&#xff0c;无需进行复杂的网络配置。 准备工作 服务器&#xff1a;一台具备公网IP的服…

软件设计师笔记-操作系统知识(二)

线程 以下是关于线程的一些关键点&#xff1a; 线程是进程中的一个实体&#xff1a;进程是操作系统分配资源&#xff08;如内存空间、文件句柄等&#xff09;的基本单位&#xff0c;而线程是进程中的一个执行单元。多个线程可以共享同一个进程的地址空间和其他资源。线程是CP…

昇思25天学习打卡营第3天|函数式自动微分

文章目录 昇思MindSpore快速入门基于MindSpore的函数式自动微分1、简介2、函数与计算图算例3、微分函数与梯度计算4、Stop Gradient&#xff08;停止梯度计算&#xff09;5、Auxiliary data6、神经网络梯度计算 Reference 昇思MindSpore快速入门 基于MindSpore的函数式自动微分…

在flask中加载mnist模型,并预测图片

一、在tensorflow中新建及保存模型 启动Jupyter Notebook 新建Notebook 生成 mnist_model.h5 模型的代码 import tensorflow as tf from tensorflow.keras.datasets import mnist from tensorflow.keras.models import Sequential from tensorflow.keras.layers import…

ASUS/华硕天选Air 2021 FX516P系列 原厂win10系统

安装后恢复到您开箱的体验界面&#xff0c;带原机所有驱动和软件&#xff0c;包括myasus mcafee office 奥创等。 最适合您电脑的系统&#xff0c;经厂家手调试最佳状态&#xff0c;性能与功耗直接拉满&#xff0c;体验最原汁原味的系统。 原厂系统下载网址&#xff1a;http:…

java设计模式(二)工厂方法模式(pattern of factory method)

1、模式介绍&#xff1a; 工厂方法模式&#xff08;pattern of factory method&#xff09;是一种创建型设计模式&#xff0c;它定义了一个用于创建对象的接口&#xff0c;但将实际创建对象的工作延迟到子类中&#xff0c;这样可以在不改变整体结构的情况下&#xff0c;通过子…

OpenGL3.3_C++_Windows(23)

伽ga马校正 物理亮度 光子数量 线性空间&#xff1a;光子数(亮度&#xff09;和颜色值的线性关系人眼感知的亮度&#xff1a;对比较暗的颜色变化更敏感&#xff0c;感知亮度基于人的感觉非线性空间&#xff1a;光子数(亮度&#xff09;和 颜色值^2.2&#xff0c;恰好符合屏幕…

GIT版本管理工具轻松入门 | TortoiseGit

目录 一、下载git 二、下载tortoisegit&#xff08;可视化git&#xff09; 三、Git本地仓库创建 四、git克隆 五、添加&#xff0c;提交&#xff0c;推送&#xff0c;拉取 六、分支 七、冲突 八、忽略文件&#xff08;修改gitignore文件&#xff09; 一、下载git 安装…

<Linux> 缓冲区谁维护?

缓冲区是谁提供的&#xff1f; 来看一段代码 #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> int main() {const char *str1 "a";printf("%s", str1);const char *str2 "b";writ…

AI副业新风口项目,AI绘画+古诗文视频,轻松吸引大批粉丝,变现简单,收益稳

前言 一个不吹牛逼不聊人生不谈理想只聊赚钱的自媒体从业者 问题都在过程中产生&#xff0c;努力做了也许会成功&#xff0c;但不做永远没有机会 — — — 小红书副业新风口项目&#xff0c;AI绘画古诗文视频&#xff0c;轻松吸引大批粉丝&#xff0c;变现简单&#xff0c;收…

2020年全国大学生数学建模竞赛C题中小微企业信贷决策(含word论文和源代码资源)

文章目录 一、部分题目二、部分论文三、部分源代码&#xff08;一&#xff09;数据处理代码&#xff08;二&#xff09;熵权法与TOPSIS代码&#xff08;三&#xff09;最小二乘法代码&#xff08;四&#xff09;粒子群代码 四、完整word版论文和源代码&#xff08;两种获取方式…

FireFox 编译指南2024 Windows10篇-环境准备(一)

1. 引言 在开源浏览器项目中&#xff0c;Firefox因其高性能和灵活性而备受开发者青睐。为了在本地环境中编译和定制Firefox&#xff0c;开发者需要做好充分的环境准备工作。这不仅是编译成功的基础&#xff0c;也是后续调试、优化和二次开发的关键步骤。 编译Firefox是一个复…

缓存双写一致性(笔记)

缓存更新方案 旁路缓存模式 这是比较多的 旁路缓存模式&#xff1a;缓存有就返回&#xff0c;没有数据库查询&#xff0c;放入缓存返回。 还有些常用缓存策略 读穿透模式 读穿透和旁路很相似&#xff0c;程序不需要关注从哪里读取数据&#xff0c;它只需要从缓存查询数据。…

PPT录屏怎么录?PPT录屏,3种方法简单操作

在数字化时代&#xff0c;PPT已经成为我们日常工作、学习和生活中不可或缺的一部分。无论是商务报告、教学课件还是产品展示&#xff0c;PPT都能帮助我们更加生动、直观地传递信息。然而&#xff0c;有时候我们会面临PPT录屏怎么录的问题。这时&#xff0c;一个好的PPT录屏功能…

OpenAI发布CriticGPT:纠错GPT生成错误代码?

OpenAI发布CriticGPT&#xff1a;纠错GPT生成错误代码&#xff1f; 前言 OCriticGPT 就在6月28日&#xff0c;OpenAI 训练出了一个基于 GPT-4 的模型—— CriticGPT&#xff0c;它可以用于查验ChatGPT 写出的代码是否出错。OpenAI的团队说&#xff0c;用户从 CriticGPT 获得帮助…

1panel 搭建多个网站

1panel 部署多个网站&#xff0c;另外的域名&#xff0c;或无域端口搭建方法。 当我们已经部署好一个网站后&#xff0c;想再部署一个网站在我们的服务器上时&#xff0c; 步骤&#xff1a;&#xff08;另外的域名&#xff0c;部署在同一个服务器方法&#xff09; 运行环境里…

营销翻车,杜国楹出面道歉,小罐茶的“大师作”故事仓皇结尾

“小罐茶&#xff0c;大师作”&#xff0c;这句slogan曾一度在央视平台长时间、高密度播放&#xff0c;成为家喻户晓的广告词&#xff0c;也打响了小罐茶品牌的名号。但同时&#xff0c;市场上关于“大师作”真实性的质疑也从未停息。 就在6月25日小罐茶十二周年发布会上&#…

光伏设计:光伏项目开发中最关键的一环

随着全球对可再生能源的需求不断增长&#xff0c;光伏技术作为其中的佼佼者&#xff0c;已经成为许多国家实现能源转型和应对气候变化的重要手段。在光伏项目的开发过程中&#xff0c;光伏设计作为最关键的一环&#xff0c;其重要性不言而喻。本文将从光伏设计的角度&#xff0…

【深度学习】单机多卡 | DataParallel将计算任务在多个 GPU 上并行执行,可以在多个 GPU 上分摊工作负载,从而加快训练速度

【深度学习】单机多卡 | DataParallel将计算任务在多个 GPU 上并行执行&#xff0c;可以在多个 GPU 上分摊工作负载&#xff0c;从而加快训练速度 写在最前面DataParallel (DP) 简介使用 DataParallel 的场景使用 DataParallel 的基本步骤 代码部分train.py简单的代码示例代码解…