OpenCV消除高亮illuminationChange函数的使用

news2024/9/23 9:32:08

学更好的别人,

做更好的自己。

——《微卡智享》

292d8839076efee2135eea0cfc2ec708.jpeg

本文长度为1129,预计阅读4分钟

导语

上一篇《OpenCV极坐标变换函数warpPolar的使用》中介绍了极坐标变换的使用,文中提到过因为手机拍的照片,部分地方反光厉害。OpenCV本身也有一个消除高亮的函数,今天这篇就是来了解一下消除高亮函数的使用,就结果来说,有效果,但不多。

773d45bbf295e56eaf31cca6869dfc88.png

实现效果

d9d4a8243b5561a6c2e9616c20f76e88.png

de5aa993534d9818e6aa3b9a12cff401.png

8eed7c039923442a8c71afa7fb582f96.png

e8aead00e727805649d0f7d3c4c27f82.png

从上几张图中可以看出,中间印的黑字的效果还明显一些,本身白字的和原来是差不多的。接下来就先说说去除高光函数illuminationChange。

illuminationChange函数

CV_EXPORTS_W void illuminationChange(InputArray src, InputArray mask, OutputArray dst,
        float alpha = 0.2f, float beta = 0.4f);

68a36f7a4cf86bc6ed1df6b2f6b66847.png

参数说明:

  • src——源图像,3通道图像

  • mask——掩膜,1通道或3通道图像都可以

  • dst——生成图像,同src

  • alpha、beta——两个参数共同决定消除高光后图像的模糊程度(范围0~2)beta越大,图片平滑越多,alpha越大,越接近原图细节。

2b93f054bba6fde75446b27180de9773.png

去高光函数比较简单,其实最核心的就是怎么样获取到掩膜,然后通过掩膜生成区域来实现去高光。具体的实现思路如下:

#去高光实现步骤
1获取图像区域,替换背景
2二值化检测高光区域
3查找高光区域轮廓生成掩膜
4进行去高光操作

其实上面的步骤来说,在去高光操作最核心的两个步骤就是通过二值化检测高光区域,和查找轮廓后生成掩膜。结合我们上一篇的代码,看看怎么样实现的。

01

获取图像区域替换背景

在我们上一篇的代码基础上,把获取圆的区域这块单独写了个函数出来,目的主要是检测到圆后,将圆的区域截取出后,因为要去高光,所以圆外对我们来说是无用区域,直接填充到黑色,这样就减少掩膜查找无用高光区域了。

c2e002948593c0e57d48103462584bf5.png

原理就是根据生成的矩形大小生成一个全黑的背景,然后将圆区域整个填充为白色,通过Mat.copyTo的方法实现。

e9f2141cc740438dbcd7e8eb24731bc8.png

02

查找高光区域

3ed86626860f271efede7567b4851fff.png

查找高光区域这里我也写成了一个函数,里面加了一步直方图均衡化,主要是本身图像清晰度也不高,所以使用直方图均衡化把图像对比度进行调整了一下,增强局部的对比度,更好的进行查找。

bbf575de9b1cdc0487b1905023f29da7.png

二值化后就是掩膜效果,为什么还要查找轮廓?

A

看到这里可能有人会提问,代码中threshold二值化找到的高光部分是不是直接可以当掩膜了,这个我开始也想直接这样的,省去了查找轮廓的部分,不过测试过程中直接报错了,也就是说去高光函数中掩膜区域里面只能是矩形,所以需要再加一步查找轮廓,针对轮廓生成外接矩形填充后才能使用

78bf7e20200e7f1fa9b02f91c9f0e01d.png

经过上面两步,去高光操作就实现了,后面的就是原图和处理后的图进行的极坐标变换对比。

完整代码

#include <iostream>
#include <opencv2/opencv.hpp>


using namespace std;
using namespace cv;


//显示窗口设置  
//参数  img 显示的图像源,
//      winname 显示的窗口名称,
//      pointx  显示的坐标x
//      pointy  显示的坐标y
void setshowwindow(Mat img, string winname, int pointx, int pointy)
{
  //设置显示窗口
  namedWindow(winname, WindowFlags::WINDOW_NORMAL);
  //设置图像显示大小
  resizeWindow(winname, img.size());
  //设置图像显示位置
  moveWindow(winname, pointx, pointy);
}




//极坐标变换
//参数 flags=INTER_LINEAR 双线性插值
Mat warpPolarMat(Mat src, int flags = INTER_LINEAR + WARP_POLAR_LINEAR) {
  // 圆心坐标
  Point2f center = Point2f(src.cols / 2, src.rows / 2);
  // 圆的半径
  double maxRadius = min(center.y, center.x) - 1;
  // 圆的周长
  int circumference = maxRadius * 2 * 3.14;


  //输出图像
  Mat dst;
  // 极坐标变换, Size()表示OpenCV根据输入自行决定输出图像尺寸
  warpPolar(src, dst, Size(0, 0), center, maxRadius, flags);


  // 改变结果方向
  rotate(dst, dst, ROTATE_90_COUNTERCLOCKWISE);


  return dst;
}


//截取圆形图像,背景改为全黑
Mat copyCircleRoi(Mat src, Point center, int radius) {
  Mat rectsrc;
  //根据圆点和半径生成矩形
  Rect rect = Rect(Point(center.x - radius, center.y - radius), Point(center.x + radius, center.y + radius));
  //截图到当前圆的图像
  Mat rectroi = src(rect);


  //截取圆形区域
  Mat circleroi = Mat::zeros(rectroi.size(), CV_8U);
  //绘制检测到的圆
  circle(circleroi, Point(circleroi.rows / 2, circleroi.cols / 2), circleroi.rows / 2, Scalar(255), -1);


  //复制截取圆形区域
  rectroi.copyTo(rectsrc, circleroi);


  return rectsrc;
}


//去除高光
Mat matilluminationChange(Mat src) {
  Mat gray, threshmat, dst;
  //复制出来改为灰度图
  cvtColor(src, gray, COLOR_BGR2GRAY);


  //直方图均衡化
  equalizeHist(gray, gray);
  //imshow("equalizeHist", gray);


  //二值化操作,定义大于210的即为高光
  threshold(gray, threshmat, 210, 255, THRESH_BINARY);


  //查找图片中高亮区域轮廓
  vector<vector<cv::Point> > contours;
  findContours(threshmat, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);


  Mat mask = Mat::zeros(src.size(), CV_8UC1);
  for (int i = 0; i < contours.size(); ++i) {
    Rect rect = boundingRect(contours[i]);
    rectangle(mask, rect, Scalar(255), -1);
  }


  //imshow("mask", mask);


  //去高光
  illuminationChange(src, mask, dst, 1.0f, 0.1f);


  return dst;
}


int main(int argc, char** argv) {


  //测试图片文件  
  string testfile = "E:/DCIM/imagetest/06.png";


  //读取图片
  Mat src = imread(testfile);
  //修改图片大小
  setshowwindow(src, "src", 50, 200);
  imshow("src", src);




  //灰度图
  Mat gray;
  cvtColor(src, gray, COLOR_BGR2GRAY);


  //中值滤波
  medianBlur(gray, gray, 9);


  //霍夫圆检测
  vector<Vec3f> circles;
  HoughCircles(gray, circles, HOUGH_GRADIENT, 1, 50, 100, 100);


  //绘制检测到的圆型
  for (size_t i = 0; i < circles.size(); ++i) {
    Vec3f item = circles[i];
    
    //获取当前圆区域,用黑底
    Mat rectsrc = copyCircleRoi(src, Point(item[0], item[1]), item[2]);
  
    //去高光操作
    Mat rectdst, showmat;
    rectdst = matilluminationChange(rectsrc);


    //极坐标变换
    Mat wpMat1, wpMat2;
    wpMat1 = warpPolarMat(rectsrc);
    wpMat2 = warpPolarMat(rectdst);




    //设置当前圆显示位置
    String title = "rectsrc" + to_string(i);
    String title2 = "rectdst" + to_string(i);
    String title3 = "wpMat1" + to_string(i);
    String title4 = "wpMat2" + to_string(i);


    //间距
    int spacing = 40;
    if ((i % 2) == 0) {
      setshowwindow(rectsrc, title, 0, rectsrc.rows * (i / 2) + spacing * (i / 2));
      setshowwindow(rectdst, title2, rectsrc.rows, rectsrc.rows * (i / 2) + spacing * (i / 2));
      setshowwindow(wpMat1, title3, rectsrc.cols * 2, rectsrc.rows * (i / 2) + spacing * (i / 2));
      setshowwindow(wpMat2, title4, rectsrc.cols * 2, rectsrc.rows * (i / 2) + wpMat1.rows + spacing * (i / 2));
    }
    else {
      setshowwindow(rectsrc, title, (rectsrc.rows * 2 + wpMat1.cols), rectsrc.rows * (i / 2) + (i % 2) + spacing * (i / 2));
      setshowwindow(rectdst, title2, (rectsrc.rows * 2 + wpMat1.cols) + rectdst.rows, rectsrc.rows * (i / 2) + (i % 2) + spacing * (i / 2));
      setshowwindow(wpMat1, title3, (rectsrc.rows * 2 + wpMat1.cols) + rectsrc.cols * 2 , rectsrc.rows * (i / 2) + (i % 2) + spacing * (i / 2));
      setshowwindow(wpMat2, title4, (rectsrc.rows * 2 + wpMat1.cols) + rectsrc.cols * 2, rectsrc.rows * (i / 2) + (i % 2) + wpMat1.rows  + spacing * (i / 2));
    }
    imshow(title, rectsrc);
    imshow(title2, rectdst);
    imshow(title3, wpMat1);
    imshow(title4, wpMat2);
    //break;
  }






  waitKey();


  return 0;
}

b4b0e8a90e6d8c0009ceaa76e2e6c82b.png

8a6d1b8f66bb87a66182601cb92d19bc.png

往期精彩回顾

 

d17fb11e916b61cff8bb83d8bda20d51.jpeg

OpenCV极坐标变换函数warpPolar的使用

 

 

1a1b3915fdee12716678cdfa5f5fa54f.jpeg

Android Aidl跨进程通讯(四)--接口回调,服务端向客户端发送数据

 

 

eed55e25169de525589f7d609c1064a8.jpeg

Android Aidl跨进程通讯(三)--进阶使用

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用yarn安装electron时手动选择版本

访问1Password或者其他可以提供随机字符的网站&#xff0c;获取随机密码运行安装命令 操作要点&#xff0c;必须触发Couldnt find any versions for "electron" that matches "*"才算成功 将复制的随机密码粘贴到后面 例如&#xff1a;yarn add --dev elec…

CAS-源码分析引出Unsafe类、Unsafe类详解

CASDemo演示 public class CASDemo {public static void main(String[] args) {AtomicInteger atomicInteger new AtomicInteger(5);System.out.println(atomicInteger.compareAndSet(5, 2022) "\t" atomicInteger.get());//true 2022System.out.println(atomicI…

vit-transfomers 逐段精读

Vision Transformer Explained | Papers With Code 有趣的特性 在cnn中处理的不太好&#xff0c;但是在transformers 都能处理的很好的例子。 Intriguing Properties of Vision Transformers | Papers With Code 标题 AN IMAGE IS WORTH 16X16 WORDS: TRANSFORMERS FOR IMAGE…

基于ETM+遥感数据的城市热岛效应现状研究的解决方案

1.引言 城市热岛效应&#xff08;Urban Heat Island Effect&#xff09;是指城市中的气温明显高于外围郊区的现象。在近地面温度图上&#xff0c;郊区气温变化很小&#xff0c;而城区则是一个高温区&#xff0c;就像突出海面的岛屿&#xff0c;由于这种岛屿代表高温的城市区域&…

【已解决】vs2015操作创建声明定义由于以下原因无法完成

本博文解决这样的一个问题&#xff0c;就是vs2015下用qt&#xff0c;在快速创建槽函数时给笔者报了个错误&#xff0c;错误的完整说法是这样子的”操作创建声明/定义“由于下列原因无法完成&#xff0c;所选的文本不包含任何函数签名。第一次遇到这种花里胡哨的问题&#xff0c…

[CVPR-23] PointAvatar: Deformable Point-based Head Avatars from Videos

[paper | code | proj] 本文的形变方法被成为&#xff1a;Forward DeformationPointAvatar基于点云表征动态场景。目标是根据给定的一段单目相机视频&#xff0c;重建目标的数字人&#xff0c;并且数字人可驱动&#xff1b;通过标定空间&#xff08;canonical space&#xff09…

Jmeter实现CSV数据批量导入

CSV&#xff1a;逗号分隔值&#xff0c;是一种简洁且常见的数据存储格式。 1、参数化&#xff1a; 在Jmeter中&#xff0c;可以通过“用户自定义的变量”来实现参数化使操作方便&#xff0c;使用语法位&#xff1a;${参数名}&#xff0c;如下图&#xff1a; 而CSV也同理&…

第二百一十八回 如何修改CircleAvatar的大小

文章目录 1. 概念介绍2. 使用方法3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 我们在上一章回中介绍了"修改页面导航中遇到的问题"沉浸式状态样相关的内容&#xff0c;本章回中将介绍如何修改avada的大小.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1.…

代码随想录算法训练营第四十一天|198.打家劫舍 ,213.打家劫舍II ,337.打家劫舍III

198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#…

支持向量机 支持向量机概述

支持向量机概述 支持向量机 Support Vector MachineSVM ) 是一类按监督学习 ( supervisedlearning)方式对数据进行二元分类的广义线性分类器 (generalized linear classifier) &#xff0c;其决策边界是对学习样本求解的最大边距超亚面 (maximum-margin hyperplane)与逻辑回归和…

第二百一十七回 修改页面导航中遇到的问题

文章目录 1. 问题介绍2. 使用方法3. 代码与分析3.1 示例代码3.2 代码分析4. 内容总结我们在上一章回中介绍了"分享一种更新页面数据的方法"相关的内容,本章回中将介绍修改页面导航中遇到的问题.闲话休提,让我们一起Talk Flutter吧。 1. 问题介绍 我们在页面之间导…

C++刷题 -- 字符串

C刷题 – 字符串 文章目录 C刷题 -- 字符串1.重复的子字符串 1.重复的子字符串 https://leetcode.cn/problems/repeated-substring-pattern/submissions/490209402/ 暴力解法 第一个for循环用来标定子串的末尾&#xff0c;根据末尾取出子串 第二个while循环用来检查原字符串是…

WEB渗透—PHP反序列化(六)

Web渗透—PHP反序列化 课程学习分享&#xff08;课程非本人制作&#xff0c;仅提供学习分享&#xff09; 靶场下载地址&#xff1a;GitHub - mcc0624/php_ser_Class: php反序列化靶场课程&#xff0c;基于课程制作的靶场 课程地址&#xff1a;PHP反序列化漏洞学习_哔哩…

AT32 F435简介4/N ChibiOS porting plan

AT32 F435简介4/N ChibiOS porting plan 1. 源由2. 框图3. Makefile4. 分析4.1 Startup Code4.2 RT Port Layer4.3 HAL Board Layer4.4 HAL Port Layer 5. 总结6. 参考资料 1. 源由 对比STM32 F405进行AT32 F435 MCU的资料研读&#xff0c;期望获取更多差异化信息&#xff1b;…

Android 架构 - 组件化

一、概念 组件化是对单个功能进行开发&#xff0c;使得功能可以复用。将多个功能组合起来就是一个业务模块&#xff0c;因此去除了模块间的耦合&#xff0c;使得按业务划分的模块成了可单独运行的业务组件。&#xff08;一定程度上的独立&#xff0c;还是依附于整个项目中&…

Python 多维数组详解(numpy)

文章目录 1 概述1.1 numpy 简介1.2 ndarray 简介 2 数组操作2.1 创建数组&#xff1a;array()2.2 裁切数组&#xff1a;切片2.3 拼接数组&#xff1a;concatenate()2.4 拆分数组&#xff1a;array_split()2.5 改变数组形状&#xff1a;reshape() 3 元素操作3.1 获取元素&#x…

uint29传输格式

前言 不知道谁想出来的。 反正我是想不到。 我看网上也没人讲这个。 写篇博客帮一下素未谋面的网友。 uint29 本质上是网络传输的时候&#xff0c;借用至多4字节Bytes&#xff0c;表达29位的无符号整数。 读8位数字&#xff0c;判断小于128? 是的话&#xff0c;返回末7位…

一文读懂PMP项目管理

PMP项目管理是什么 PMP&#xff08;Project Management Professional&#xff09;指项目管理专业人员资格认证&#xff0c;由美国项目管理协会&#xff08;Project Management Institute&#xff0c;简称PMI&#xff09;发起&#xff0c;目前已在全球206个国家和地区进行认证&…

Axure中继器的基本使用

介绍中继器 在 Axure 中&#xff0c;中继器是一种交互设计元素&#xff0c;用于在不同页面之间传递数据或触发特定的事件。它可以帮助模拟真实的用户交互流程和页面之间的传递逻辑&#xff0c;继承关系用于描述两个元件之间的父子关系。通过使用继承关系&#xff0c;您可以创建…

实现高效、透明、公正的采购寻源——鸿鹄电子招投标系统

在数字化时代&#xff0c;企业需要借助先进的数字化技术来提高工程管理效率和质量。招投标管理系统作为企业内部业务项目管理的重要应用平台&#xff0c;涵盖了门户管理、立项管理、采购项目管理、采购公告管理、考核管理、报表管理、评审管理、企业管理、采购管理和系统管理等…