20 霍夫圆检测
1 原理简介 2 相关API
①原理简介
对于笛卡尔坐标系中圆的方程为 也就是 即 对于圆而言,只要圆心a,b相等,半径r相等,那么就在同一个圆上。经过xy坐标系映射到abr的坐标系上,会形成很多三维的曲线,多个曲线相交于三维坐标系abr的同一个点,通过判断abr坐标系中每一点的相交数量,大于一定阈值的点就是xy坐标系上的圆。 实际上,为了减少计算量,OpenCV基于图像梯度:
- 检测边缘Canny,发现可能的圆心
- 基于第一步的基础上从候选圆心开始计算最佳半径的大小
②相关API cv::HoughCircles
vector<Vec3f> circles;
HoughCircles(
InputArray image, //输入图像,必须是8位的单通道灰度图像
OutputArray circles, //输出结果,发现的圆信息
Int method, //方法-HOUGH_GRADIENT
Double dp, //dp=1尺度,1就是原图,2是图片减一半
Double mindist, //10 最短距离,可以分辨两个圆,否则认为是同心圆
Double param1, //Canny边缘检测的第一阈值
Double param2, //中心点累加器阈值-候选圆心
Int minradius, //最小半径
Int maxradius //最大半径
);
for(size_t i=0; i<circles.size();i++)
{
Vec3f cc = circles[i];
circle(dst, Point(cc[0], cc[1]), cc[2], Scalar(0, 0, 255), 2, LINE_AA);
circle(dst, Point(cc[0], cc[1]), 2, Scalar(0, 0, 255), 2, LINE_AA);
}
21 像素重映射
1 什么是像素重映射 2 API介绍
①什么是像素重映射
- 就是把输入图像中各个像素按照一定的规则映射到另一张图像的对应位置上去,形成一张新的图像。
②相关API
Remap(
InputArray src, //输入图像
OutputArray dst, //输出图像
InputArray map1, //x映射表 CV_32FC1/CV_32FC2
InputArray map2, //y映射表
int interpolation, //选择的差值方法,常见线性插值,可选择立方等
int borderMode, //BORDER_CONSTANT
const Scalar borderValue //color
)
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位或者浮点型图像。 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放函数调用后的输出结果,需和源图片有一样的尺寸和类型 第三个参数,InputArray类型的map1,它有两种可能的表示对象。表示点(x,y)的第一个映射。表示CV_16SC2 , CV_32FC1 或CV_32FC2类型的X值。 第四个参数,InputArray类型的map2,同样,它也有两种可能的表示对象,而且他是根据map1来确定表示那种对象。若map1表示点(x,y)时。这个参数不代表任何值。表示CV_16UC1 , CV_32FC1类型的Y值(第二个值)。 第五个参数,int类型的interpolation,插值方式,之前的resize( )函数中有讲到,需要注意,resize( )函数中提到的INTER_AREA插值方式在这里是不支持的,所以可选的插值方式如下:INTER_NEAREST – 最近邻插值INTER_LINEAR – 双线性插值(默认值)INTER_CUBIC – 双三次样条插值(逾4×4像素邻域内的双三次插值)INTER_LANCZOS4 -Lanczos插值(逾8×8像素邻域的Lanczos插值)。 第六个参数,int类型的borderMode,边界模式,有默认值BORDER_CONSTANT. 第七个参数,const Scalar&类型的borderValue,当有常数边界时使用的值,其默认值为0。
void updata_map() {
for (int row = 0; row < img.rows; row++)
{
for (int col = 0; col < img.cols; col++)
{
switch (index)
{
//index = 0 ,图像的行跟列为为原来的1/2。
//index = 1,为左右翻转(列变换,行不变)
//index = 2,为上下翻转(行变换,列不变)
//index = 3,为中心旋转
case 0:
if (col > (img.cols*0.25) && col<(img.cols*0.75) && row>(img.rows*0.25) && row < (img.rows*0.75)) {
map_x.at<float>(row, col) = 2 * (col - (img.cols*0.25));
map_y.at<float>(row, col) = 2 * (row - (img.rows*0.25) - 0.25);
}
else
{
map_x.at<float>(row, col) = 0;
map_y.at<float>(row, col) = 0;
}
break;
case 1:
map_x.at<float>(row, col) = (img.cols - col - 1);
map_y.at<float>(row, col) = row;
break;
case 2:
map_x.at<float>(row, col) = col;
map_y.at<float>(row, col) = (img.rows - row - 1);
break;
case 3:
map_x.at<float>(row, col) = (img.cols - col - 1);
map_y.at<float>(row, col) = (img.rows - row - 1);
break;
default:
break;
}
}
}
22 直方图均衡化
1 什么是直方图 2 直方图均衡化 3 API说明
①什么是直方图(Histogram)
图像直方图,是指对整个图像在灰度范围内的像素值(0~255)统计出现频率次数,据此生成的直方图,成为图像直方图。直方图反映了图像灰度的分布情况。是图像的统计学特征。
②直方图均衡化
直方图均衡化是一种提高图像对比度的方法,拉伸图像灰度值范围 通过remap我们可以将图像灰度分布从一个分布映射到另外一个分布,如何再得到映射后的像素值即可。
③相关API
equalizeHist(
InputArray src, //输入图像,必须是8位单通道图像
OutputArray dst, //输出结果
)
23 直方图计算
1 概念 2 相关API
①直方图概念续
- 上述直方图概念是基于图像像素值,其实对图像梯度、每个像素的角度等一切图像的属性值,我们都可以建立直方图。基于图像像素灰度直方图是最常见的。
- 直方图最常见的几个属性:
- dims:表示维度,对灰度图像来说只有一个通道值dims=1
- bins:表示在维度中子区域大小划分
- range:表示值的范围,灰度值范围为[0~255]之间
②相关API
把多通道图像分为多个单通道图像:
split(
const Mat &src, //输入图像
Mat* mvbegin //输出的单通道图像数组
)
//分通道显示
vector<Mat> bgr_graphs;
split(src, bgr_graphs);
直方图计算:
calcHist(
const Mat* images, //输入图像指针
int images, //图像数目
const int* channels, //通道数
InputArray mask, //输入mask,可选
OutputArray hist, //输出的直方图数据
int dims, //维数
const int* histsize, //直方图级数
const float* ranges, //值域范围
bool uniform, //默认为True
bool accumulate //默认为False
)
③示例
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat src = imread("D:/vcprojects/images/test.png");
if (!src.data) {
printf("could not load image...\n");
return -1;
}
char INPUT_T[] = "input image";
char OUTPUT_T[] = "histogram demo";
namedWindow(INPUT_T, CV_WINDOW_AUTOSIZE);
namedWindow(OUTPUT_T, CV_WINDOW_AUTOSIZE);
imshow(INPUT_T, src);
vector<Mat> bgr_planes;
split(src, bgr_planes);
//imshow("single channel demo", bgr_planes[0]);
int histSize = 256;
float range[] = { 0, 256 };
const float *histRanges = { range };
Mat b_hist, g_hist, r_hist;
calcHist(&bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRanges, true, false);
calcHist(&bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRanges, true, false);
calcHist(&bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRanges, true, false);
int hist_h = 400;
int hist_w = 512;
int bin_w = hist_w / histSize;
Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));
normalize(b_hist, b_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
normalize(g_hist, g_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
normalize(r_hist, r_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
// render histogram chart
for (int i = 1; i < histSize; i++) {
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(b_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, LINE_AA);
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(g_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), 2, LINE_AA);
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(r_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, LINE_AA);
}
imshow(OUTPUT_T, histImage);
waitKey(0);
return 0;
}
24 直方图比较
1 直方图比较方法 2 相关API
①直方图比较方法概述
对输入的两张图像计算得到直方图H1与H2,归一化到相同的尺度空间然后可以通过计算H1与H2的之间的距离得到两个直方图的相似程度进而比较图像本身的相似程度。OpenCV提供的比较方法有四种(下面出现的H ^ \hat{H}H^都表示均值):
- Correlation 相关性比较
- CV_COMP_CORREL
- Chi-Square 卡方比较
- CV_COMP_CHISQR
-
- 如果H1和H2相同结果为0。
- Intersection 十字交叉性
- CV_COMP_INTERSECT
- CV_COMP_BHATTACHARYYA
②相关API
- 首先将图像从RGB色彩空间转换到HSV色彩空间(cvtColor)
- 计算图像的直方图,然后归一化到[0~1]之间(calcHist和normalize)
- 使用上述四种方法之一进行比较(compareHist)
- 结果是一个double的类型
comparHist(
InputArray H1,
InputArray H2,
int method //比较方法
);
25 直方图反向投影(Back Projection)
1 反向投影 2 实现步骤与相关API
①反向投影
- 反向投影是反映直方图模型在目标图像中的分布情况
- 就是用直方图模型去目标图像中寻找是否有相似的对象。通常用HSV色彩空间的HS两个通道直方图模型
设有原灰度图像矩阵: 把图像划分为[0-2]、[3-5]、[5-7]、[7-9]四个区间,那么直方图Hist=4 4 6 2,下面计算反向投影矩阵(用Hist直方图的值替代掉原灰度图像矩阵的值,如第一个点在[0-2]区间内,替换成4): 实际上是将图像的256个灰度值置为很少的几个值了,具体有几个值,要看把0~255划分为多少个区间!反向投影中某点的值就是它对应的原图像中的点所在区间的灰度直方图值。所以,一个区间点越多,在反向投影矩阵中就越亮。反向投影中的“反向”指的是从直方图值到反向投影矩阵映射的过程。 通过反向投影,原始的图像被简化了,而这个简化的过程实际上就是提取出图像的某个特征。所以我们就可以用这个特征来对比两幅图,如果两幅图的反向投影矩阵相似或相同,那么我们就可以判定这两幅图这个特征是相同的。
②相关API
- 建立直方图模型
- 计算待测图像直方图并映射到模型中
- 从模型反向计算生成图像
实现步骤:
- 加载图片imread
- 将图像从RGB色彩空间转换到HSV色彩空间cvtColor
- 计算直方图和归一化calcHist与normalize
- Mat与MatND其中Mat表示二维数组,MatND表示三维或者多维数据,此处均可以用Mat表示
- 计算反向投影图像calcBackProject
③示例
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
Mat src; Mat hsv; Mat hue;
int bins = 12;
void Hist_And_Backprojection(int, void*);
int main(int argc, char** argv) {
src = imread("D:/vcprojects/images/t1.jpg");
if (src.empty()) {
printf("could not load image...\n");
return -1;
}
const char* window_image = "input image";
namedWindow(window_image, CV_WINDOW_NORMAL);
namedWindow("BackProj", CV_WINDOW_NORMAL);
namedWindow("Histogram", CV_WINDOW_NORMAL);
cvtColor(src, hsv, CV_BGR2HSV);
hue.create(hsv.size(), hsv.depth());
int nchannels[] = { 0, 0 };
mixChannels(&hsv, 1, &hue, 1, nchannels, 1);
createTrackbar("Histogram Bins:", window_image, &bins, 180, Hist_And_Backprojection);
Hist_And_Backprojection(0, 0);
imshow(window_image, src);
waitKey(0);
return 0;
}
void Hist_And_Backprojection(int, void*) {
float range[] = { 0, 180 };
const float *histRanges = { range };
Mat h_hist;
calcHist(&hue, 1, 0, Mat(), h_hist, 1, &bins, &histRanges, true, false);
normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());
Mat backPrjImage;
calcBackProject(&hue, 1, 0, h_hist, backPrjImage, &histRanges, 1, true);
imshow("BackProj", backPrjImage);
int hist_h = 400;
int hist_w = 400;
Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));
int bin_w = (hist_w / bins);
for (int i = 1; i < bins; i++) {
rectangle(histImage,
Point((i - 1)*bin_w, (hist_h - cvRound(h_hist.at<float>(i - 1) * (400 / 255)))),
//Point(i*bin_w, (hist_h - cvRound(h_hist.at<float>(i) * (400 / 255)))),
Point(i*bin_w, hist_h),
Scalar(0, 0, 255), -1);
}
imshow("Histogram", histImage);
return;
}
mixChannels函数:用于将输入数组的指定通道复制到输出数组的指定通道。
void mixChannels(
const Mat* src, //输入数组或向量矩阵,所有矩阵的大小和深度必须相同。
size_t nsrcs, //矩阵的数量
Mat* dst, //输出数组或矩阵向量,大小和深度必须与src相同
size_t ndsts,//矩阵的数量
const int* fromTo,//指定被复制通道与要复制到的位置组成的索引对
size_t npairs //fromTo中索引对的数目
);
26 模板匹配(Template Match)
1 模板匹配介绍 2 相关API
①模板匹配介绍
- 模板匹配就是在整个图像区域发现与给定子图像匹配的小块区域;
- 模板匹配需要首先给定一个模板图像;
- 另外需要一张待检测的图像;
- 工作方法:在待检测图像上,从左到右,从上到下计算模板图像与重叠子图像的匹配度,匹配程度越大,两者相同的可能性越大。
OpenCV提供的几种方法:
- 计算平方不同 TM_SQDIFF
-
- 值越小越相似
- 计算相关性 TM_SQDIFF_NORMED
- 计算相关系数 TM_CCORR
-
- 值越接近于0,越相似
- 计算归一化相关性 TM_CCOEFF
-
- 值越接近于1,越相似
- 计算归一化相关系数 TM_CCOEFF_NORMED
②相关API
matchTemplate(
InputArray image, //原图像,必须是8-bit或者32-bit浮点数图像
InputArray templ, //模板图像,类型与输入图像一致
OutputArray result, //输出结果,必须是单通道32位浮点数,假设原图像W*H,模板图像w*h,结果必须为W-w+1,H-h+1的大小
int method, //使用的匹配方法
InputArray mask=noArray()
)
enum cv::TemplateMatchModes {
cv::TM_SQDIFF = 0,
cv::TM_SQDIFF_NORMED = 1,
cv::TM_CCORR = 2,
cv::TM_CCORR_NORMED = 3,
cv::TM_CCOEFF = 4,
cv::TM_CCOEFF_NORMED = 5
}
minMaxLoc(src, minVal, maxVal, minLoc, maxLoc, mask)
/*
在一个数组中找到全局最小值和全局最大值
minMaxLoc函数找到最小值和最大值元素值以及它们的位置。
*/
③示例代码
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
Mat src, temp, dst;
int match_method = TM_SQDIFF;
int max_track = 5;
const char* INPUT_T = "input image";
const char* OUTPUT_T = "result image";
const char* match_t = "template match-demo";
void Match_Demo(int, void*);
int main(int argc, char** argv) {
// 待检测图像
src = imread("D:/vcprojects/images/flower.png");
// 模板图像
temp = imread("D:/vcprojects/images/t2.png");
if (src.empty() || temp.empty()) {
printf("could not load image...\n");
return -1;
}
namedWindow(INPUT_T, CV_WINDOW_AUTOSIZE);
namedWindow(OUTPUT_T, CV_WINDOW_NORMAL);
namedWindow(match_t, CV_WINDOW_AUTOSIZE);
imshow(INPUT_T, temp);
const char* trackbar_title = "Match Algo Type:";
createTrackbar(trackbar_title, OUTPUT_T, &match_method, max_track, Match_Demo);
Match_Demo(0, 0);
waitKey(0);
return 0;
}
void Match_Demo(int, void*) {
int width = src.cols - temp.cols + 1;
int height = src.rows - temp.rows + 1;
Mat result(width, height, CV_32FC1);
matchTemplate(src, temp, result, match_method, Mat());
normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());
Point minLoc;
Point maxLoc;
double min, max;
src.copyTo(dst);
Point temLoc;
minMaxLoc(result, &min, &max, &minLoc, &maxLoc, Mat());
if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED) {
temLoc = minLoc;
} else {
temLoc = maxLoc;
}
// 绘制矩形
rectangle(dst, Rect(temLoc.x, temLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255), 2, 8);
rectangle(result, Rect(temLoc.x, temLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255), 2, 8);
imshow(OUTPUT_T, result);
imshow(match_t, dst);
}