opencv-寻找图像最清晰区域
应朋友一个小小的需求,需要在一幅图像中找出最清晰的一个区域,由于我毕设课题刚好试自动对焦这一块
所以这个对我来说,可以用opencv简单的实现一下。
所谓最清晰的区域,也就是用图像评价函数所得的值最大的感兴趣区域。
这里有两种区分:
1.指定感兴趣区域大小,然后在原图像不断的滑动,获取对焦评价值。将rect 和对焦评价值value
存储在一个哈希表中,然后寻找哈希表value最大对应的key值。
2.不知道感兴趣大小,直接找对焦清晰区域,这种的话目前我的想法就是小区域寻找,然后近似值
区域拼接,当然也可以用CNN等深度学习方法,前提是你得懂。(目前:我也不是很擅长)
1.第一步
先将对焦评价值封装在一个函数中
这里可以参考牧野的博客:
OpenCV 图像清晰度评价(相机自动对焦):https://blog.csdn.net/dcrmg/article/details/53543341
这里他主要提供了基本的三种方法:
分别是Tenengrad梯度方法、Laplacian梯度方法和方差方法
就我个人目前研究来说,推荐使用Tenengrad方法(当然有更好的)
它的代码如下:
#include <highgui/highgui.hpp>
#include <imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat imageSource = imread("2.jpg");
Mat imageGrey;
cvtColor(imageSource, imageGrey, CV_RGB2GRAY);
Mat imageSobel;
Sobel(imageGrey, imageSobel, CV_16U, 1, 1);
//图像的平均灰度
double meanValue = 0.0;
meanValue = mean(imageSobel)[0];
//double to string
stringstream meanValueStream;
string meanValueString;
meanValueStream << meanValue;
meanValueStream >> meanValueString;
meanValueString = "Articulation(Sobel Method): " + meanValueString;
putText(imageSource, meanValueString, Point(20, 50), CV_FONT_HERSHEY_COMPLEX, 0.8, Scalar(255, 255, 25), 2);
imshow("Articulation", imageSource);
waitKey();
}
简单将其封装一下得到我们的获取对焦值函数
float AF_Score(Mat srcImage)
{
Mat imageGrey;
cvtColor(srcImage, imageGrey, CV_RGB2GRAY);
Mat imageSobel;
Sobel(imageGrey, imageSobel, CV_16U, 1, 1);
//图像的平均灰度
double af_score = 0.0;
af_score = mean(imageSobel)[0];
return af_score;
}
2.第二步
然后就是遍历图像进行获取对焦值并将其封装在map中。
int ROI_Width = 500;
int ROI_Height = 500;
Mat RoiImage;
map<MyStruct, float> tMap;
vector<pair<MyStruct, float>> tVector;
void SaveROIValue(Mat sourceImage)
{
int nr = sourceImage.rows;
int nc = sourceImage.cols;
for (int i = 0; i<(nc-ROI_Height); i+=10)
{
for (int j = 0; j < (nr - ROI_Width); j+=10)
{
Rect rect_roi(i, j, ROI_Width, ROI_Height);
sourceImage(rect_roi).copyTo(RoiImage);
float tempvalue = AF_Score(RoiImage);
tMap.insert(make_pair(MyStruct(i, j, ROI_Width, ROI_Height), tempvalue));
}
}
}
备注一下:其中ROI_Width与ROI_Height是在工程开始定义的,为感兴趣ROI的大小,roiImage也是定义的一个
Mat对象(i, j 每次循环的变化量就是移动的步长,可以控制一下,要不然很慢)。
这里值得注意的地方就是我的tMap的key值是一个结构体,为什么要定义成一个结构体呢?
起初我是想直接用opencv的rect数据结构作为key值,发现一直不得行,应该是不支持吧!
后面经过一番百度之后发现原因,原博客链接如下:
[C++]关于map的Key值:
https://blog.csdn.net/shen_rong/article/details/10823391
我们知道,对于map的键类型,唯一的约束是必须支持<操作符,至于是否支持其他的关系或相等运算,则不做要求
如果map的Key为内置类型,则通常已支持<操作,比如map,int类型已经支持<
如果是自定义类型呢?则必须重载<!(PS:其实我并不知道)
于是我按照他的方法定义了一个类,有四个成员,分别代表rect的(x, y, width, height)。我们就是将这样一个结构体作为哈希表的key值,然后找到了最大value对应的key值之后再将其成员赋给opencv的rect容器即可。
结构体定义如下:
class MyStruct
{
public:
int x;
int y;
int width;
int height;
MyStruct(int ix = 0, int iy = 0, int iw = 0, int ih=0)
{
x = ix;
y = iy;
width = iw;
height = ih;
}
// friend bool operator< (const MyStruct &ms1, const MyStruct &ms)
// {
// return ms1.x < ms.x && ms1.y < ms.y && ms1.width < ms.width && ms1.height < ms.height;
// }
friend bool operator< (const MyStruct &ms1, const MyStruct &ms)
{
if (ms1.x != ms.x)
{
return ms1.x < ms.x;
}
if (ms1.y != ms.y)
{
return ms1.y < ms.y;
}
if (ms1.width != ms.width)
{
return ms1.width < ms.width;
}
if (ms1.height != ms.height)
{
return ms1.height < ms.height;
}
return false;
}
};
3.第三步
接下来就是对哈希表按照value值排序,找到value值最大对应的key。代码如下:
bool cmp(const pair<MyStruct, float>& x, const pair<MyStruct, float>& y)
{
return x.second > y.second;
}
void sortMapByValue(map<MyStruct, float>& tMap, vector<pair<MyStruct, float> >& tVector)
{
for (map<MyStruct, float>::iterator curr = tMap.begin(); curr != tMap.end(); curr++)
tVector.push_back(make_pair(curr->first, curr->second));
sort(tVector.begin(), tVector.end(), cmp);
}
4.第四步
最后一步就是将找到的矩形区域在原图像上给画出来然后将对应的值标记一下
Point getCenterPoint(Rect rect)
{
Point cpt;
cpt.x = rect.x + cvRound(rect.width / 2.0);
cpt.y = rect.y + cvRound(rect.height / 2.0);
return cpt;
}
void DrawAndPutText(Mat sourceImage, Rect rect ,float afvalue)
{
//矩形框颜色以及线条粗细
Scalar color = Scalar(0, 0, 255);
int linewidth = 2;
rectangle(sourceImage, rect, color, linewidth);
Point centerPoint = getCenterPoint(rect);
char tam[100];
int LeftUpX = rect.x;
int LeftUpY = rect.y;
sprintf(tam, "Value%.2f[%d,%d,%d,%d]", afvalue, LeftUpX, LeftUpY, rect.width, rect.height);
putText(sourceImage, tam, centerPoint, FONT_HERSHEY_SIMPLEX, 0.7, Scalar(255, 0, 255), 1);
}
完整的工程如下:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <cstdlib>
#include <algorithm>
using namespace std;
using namespace cv;
int ROI_Width = 500;
int ROI_Height = 500;
class MyStruct
{
public:
int x;
int y;
int width;
int height;
MyStruct(int ix = 0, int iy = 0, int iw = 0, int ih=0)
{
x = ix;
y = iy;
width = iw;
height = ih;
}
// friend bool operator< (const MyStruct &ms1, const MyStruct &ms)
// {
// return ms1.x < ms.x && ms1.y < ms.y && ms1.width < ms.width && ms1.height < ms.height;
// }
friend bool operator< (const MyStruct &ms1, const MyStruct &ms)
{
if (ms1.x != ms.x)
{
return ms1.x < ms.x;
}
if (ms1.y != ms.y)
{
return ms1.y < ms.y;
}
if (ms1.width != ms.width)
{
return ms1.width < ms.width;
}
if (ms1.height != ms.height)
{
return ms1.height < ms.height;
}
return false;
}
};
Mat RoiImage;
map<MyStruct, float> tMap;
vector<pair<MyStruct, float>> tVector;
float AF_Score(Mat srcImage)
{
Mat imageGrey;
cvtColor(srcImage, imageGrey, CV_RGB2GRAY);
Mat imageSobel;
Sobel(imageGrey, imageSobel, CV_16U, 1, 1);
//图像的平均灰度
double af_score = 0.0;
af_score = mean(imageSobel)[0];
return af_score;
}
bool cmp(const pair<MyStruct, float>& x, const pair<MyStruct, float>& y)
{
return x.second > y.second;
}
void sortMapByValue(map<MyStruct, float>& tMap, vector<pair<MyStruct, float> >& tVector)
{
for (map<MyStruct, float>::iterator curr = tMap.begin(); curr != tMap.end(); curr++)
tVector.push_back(make_pair(curr->first, curr->second));
sort(tVector.begin(), tVector.end(), cmp);
}
void SaveROIValue(Mat sourceImage)
{
int nr = sourceImage.rows;
int nc = sourceImage.cols;
for (int i = 0; i<(nc-ROI_Height); i+=100)
{
for (int j = 0; j < (nr - ROI_Width); j+=100)
{
Rect rect_roi(i, j, ROI_Width, ROI_Height);
sourceImage(rect_roi).copyTo(RoiImage);
float tempvalue = AF_Score(RoiImage);
tMap.insert(make_pair(MyStruct(i, j, ROI_Width, ROI_Height), tempvalue));
}
}
}
Point getCenterPoint(Rect rect)
{
Point cpt;
cpt.x = rect.x + cvRound(rect.width / 2.0);
cpt.y = rect.y + cvRound(rect.height / 2.0);
return cpt;
}
void DrawAndPutText(Mat sourceImage, Rect rect ,float afvalue)
{
//矩形框颜色以及线条粗细
Scalar color = Scalar(0, 0, 255);
int linewidth = 2;
rectangle(sourceImage, rect, color, linewidth);
Point centerPoint = getCenterPoint(rect);
char tam[100];
int LeftUpX = rect.x;
int LeftUpY = rect.y;
sprintf(tam, "Value%.2f[%d,%d,%d,%d]", afvalue, LeftUpX, LeftUpY, rect.width, rect.height);
putText(sourceImage, tam, centerPoint, FONT_HERSHEY_SIMPLEX, 0.7, Scalar(255, 0, 255), 1);
}
int main()
{
Mat sourceImage = imread("F4.0 工作对焦物距36mm 增大曝光-2.jpg");
if (sourceImage.empty())
{
cout << "where is your picture" << endl;
}
SaveROIValue(sourceImage);
sortMapByValue(tMap, tVector);
int x = tVector[0].first.x;
int y = tVector[0].first.y;
Rect bestrect(x, y, ROI_Width,ROI_Height);
float bestvalue = tVector[0].second;
DrawAndPutText(sourceImage, bestrect, bestvalue);
//imshow("souceImage", sourceImage);
imwrite("sourceImage.bmp", sourceImage);
waitKey();
return 0;
}
最后效果如下:
最开始是想用MFC写一个GUI,后面他说不用,这里有一个MFC利用opencv打开图片截图的程序
,在上面改进一下就可以实现了。
需要的话,留言我用邮箱发给你们!
欢迎大家交流讨论!^_^.