一个简单的例子
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main(int argc, char **argv)
{
Mat img = imread(argv[1]);
Mat map_x(img.size(), CV_32FC1);
Mat map_y(img.size(), CV_32FC1);
//map_x和map_y分别存储的是水平和竖直方向的变换矩阵
Mat result;
for(int i=0; i(i, j) = (float)(img.rows - i);
map_x.at(i, j) = (float)(j);
}
}
remap(img, result, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
imshow("upside down", result);
waitKey(0);
return 0;
}
仿射变换原理
仿射变换可以实现图像变换,旋转,尺度变换等,其中仿射矩阵有6个参数,故只需要3个对应点即可求出仿射矩阵,进而将仿射矩阵应用在原图上
//原图上三个点
Point2f srcTri[3];
srcTri[0] = Point2f( 0.f, 0.f );
srcTri[1] = Point2f( src.cols - 1.f, 0.f );
srcTri[2] = Point2f( 0.f, src.rows - 1.f );
//对应仿射图三个点
Point2f dstTri[3];
dstTri[0] = Point2f( 0.f, src.rows*0.33f );
dstTri[1] = Point2f( src.cols*0.85f, src.rows*0.25f );
dstTri[2] = Point2f( src.cols*0.15f, src.rows*0.7f );
//得到仿射矩阵
Mat warp_mat = getAffineTransform( srcTri, dstTri );
Mat warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
//warp_dst.size是仿射变换之后图片大小
warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
如果已知旋转中心和旋转角度,可以通过下面函数获得仿射矩阵
//scale 尺度变换
Mat rot_mat = getRotationMatrix2D( center, angle, scale );
equalizeHist( src, dst );
split( src, bgr_planes ); //拆分通道
calcHist( &bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate ); //统计直方图
normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() ); //正则化函数
double base_base = compareHist( hist_base, hist_base, compare_method );// 对比两幅图片的直方图相似程度
原理链接
反向投影:首先计算某一特征的直方图模式,但存在新的图像时使用模型寻找图像中符合该特征的地方
几个关键函数
//计算直方图函数
void calcHist(const Mat* arrays, int narrays, const int* channels, InputArray mask, SparseMat&hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=
false );
// arrays 输入图像一幅或多幅
// narrays 图像数量
// channels eg int channels[3] = {3, 2, 0}表示直方图是三维的,第一维是对应图像的第三通道
// Mask 掩膜
// hist输出结果
// dims 直方图维度
// histSize 每一位读直方图的个数 相当于那个竖条
// ranges 统计的范围
// uniform竖条宽度是否相等
// accumulate 多个图像 是否累计像素数量
// 反向投影的函数,参数含义与calcHist一致
calcBackProject()
完整代码
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
void Hist_and_Backproj(int, void*);
Mat hue;
// 多个灰度级别
int bins = 25;
int main()
{
Mat src = imread("2.png");
Mat hsv;
cvtColor(src, hsv, COLOR_BGR2HSV);
hue.create(hsv.size(), hsv.depth());
int ch[] = {0, 0};
mixChannels(&hsv, 1, &hue, 1, ch, 1);
namedWindow("source image");
createTrackbar("hue bins", "source image", &bins, 180, Hist_and_Backproj);
Hist_and_Backproj(0, 0);
imshow("source image", src);
waitKey();
return 0;
}
void Hist_and_Backproj(int, void*)
{
int histSize = MAX( bins, 2 );
float hue_range[] = { 0, 180 };
// calcHist的类型是因为clacHist函数定义决定 类似指针的指针
const float* ranges = { hue_range };
Mat hist;
calcHist( &hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false );
normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
// 反向投影
Mat backproj;
calcBackProject( &hue, 1, 0, hist, backproj, &ranges, 1, true );
imshow( "BackProj", backproj );
//绘制直方图 w h分辨代表宽度和长度
int w = 400, h = 400;
int bin_w = cvRound( (double) w / histSize );
Mat histImg = Mat::zeros( h, w, CV_8UC3 );
for (int i = 0; i < bins; i++)
{
// 坐标原点位于左上方
rectangle( histImg, Point( i*bin_w, h ), Point( (i+1)*bin_w, h - cvRound( hist.at(i)*h/255.0 ) ),
Scalar( 0, 0, 255 ), FILLED );
}
imshow( "Histogram", histImg );
}
void MatchingMethod( int, void* )
{
Mat img_display;
img.copyTo( img_display );
// 模板匹配之后结果图的长和宽
int result_cols = img.cols - templ.cols + 1;
int result_rows = img.rows - templ.rows + 1;
result.create( result_rows, result_cols, CV_32FC1 );
// matchTemplate 中提供了几个匹配方法,其中TM_SQDIFF和TM_CCORR_NORMED需要mask
bool method_accepts_mask = (TM_SQDIFF == match_method || match_method == TM_CCORR_NORMED);
if (use_mask && method_accepts_mask)
{ matchTemplate( img, templ, result, match_method, mask); }
else
{ matchTemplate( img, templ, result, match_method); }
//归一化结果
normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
double minVal; double maxVal; Point minLoc; Point maxLoc;
Point matchLoc;
// 寻找模板匹配结果中最大最低的值
minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
// 这两种方法寻找的是最低的值为匹配效果最好 其余方法是最大的值
if( match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED )
{ matchLoc = minLoc; }
else
{ matchLoc = maxLoc; }
//画矩形标出模板匹配的结果
rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
imshow( image_window, img_display );
imshow( result_window, result );
return;
}
void thresh_callback(int, void* )
{
Mat canny_output;
Canny( src_gray, canny_output, thresh, thresh*2 );
// 返回图像的轮廓向量, 每一个向量也都是一个轮廓,即点的向量
vector > contours;
// 返回不同轮廓的层次关系,与寻找轮廓的方法有关,非必须
vector hierarchy;
// RETR_TREE是轮廓检索模式 代表提取所有轮廓并建立网状结构
findContours( canny_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE );
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
for( size_t i = 0; i< contours.size(); i++ )
{
// rng随机数
Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );
/***
其中第一个参数image表示目标图像,
第二个参数contours表示输入的轮廓组
第三个参数contourIdx指明画第几个轮廓,如果该参数为负值,则画全部轮廓,
第四个参数color为轮廓的颜色,
第五个参数thickness为轮廓的线宽,如果为负值或CV_FILLED表示填充轮廓内部,
第六个参数lineType为线型,
第七个参数为轮廓结构信息,
第八个参数为maxLevel
***/
drawContours( drawing, contours, (int)i, color, 2, LINE_8, hierarchy, 0 );
}
imshow( "Contours", drawing );
}
简单的理解凸包就是寻找一个包含所有点的橡皮圈
void thresh_callback(int, void* )
{
Mat canny_output;
Canny( src_gray, canny_output, thresh, thresh*2 );
// 这里求轮廓没有考虑层次之间的关系
vector > contours;
findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );
// 利用convexHull求出所有在轮廓上的点凸包 hull相当于存储凸包各个点
// convexHull默认的两个参数 输出凸包方向 bool clockwise = false, 返回点or指针 bool returnPoints = true
vector >hull( contours.size() );
for( size_t i = 0; i < contours.size(); i++ )
{
convexHull( contours[i], hull[i] );
}
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
for( size_t i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );
drawContours( drawing, contours, (int)i, color );
drawContours( drawing, hull, (int)i, color );
}
imshow( "Hull demo", drawing );
}
void thresh_callback(int, void* )
{
Mat canny_output;
Canny( src_gray, canny_output, thresh, thresh*2 );
//不需要层次关系的轮廓
vector > contours;
findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );
// 多边形的顶点,对每一个轮廓都生成一个多边形
vector > contours_poly( contours.size() );
vector boundRect( contours.size() );
vectorcenters( contours.size() );
vectorradius( contours.size() );
// 寻找轮廓的最小矩形,圆形
for( size_t i = 0; i < contours.size(); i++ )
{
// 3代表精度,拟合曲线和原始曲线之间的距离
approxPolyDP( contours[i], contours_poly[i], 3, true );
boundRect[i] = boundingRect( contours_poly[i] );
minEnclosingCircle( contours_poly[i], centers[i], radius[i] );
}
// 绘图
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
for( size_t i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );
drawContours( drawing, contours_poly, (int)i, color );
rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2 );
circle( drawing, centers[i], (int)radius[i], color, 2 );
}
imshow( "Contours", drawing );
}
类似的可以求椭圆和倾斜的矩形(面积最小)作为包围盒
void thresh_callback(int, void* )
{
Mat canny_output;
Canny( src_gray, canny_output, thresh, thresh*2 );
vector > contours;
findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );
vector minRect( contours.size() );
vector minEllipse( contours.size() );
for( size_t i = 0; i < contours.size(); i++ )
{
minRect[i] = minAreaRect( contours[i] );
// 函数的输入为所有点坐标,返回值为矩形四个顶点的坐标
//椭圆的拟合至少需要6个点,
if( contours[i].size() > 5 )
{
minEllipse[i] = fitEllipse( contours[i] );
// 拟合得到的是椭圆的最小外接矩形
}
}
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
for( size_t i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );
// 轮廓
drawContours( drawing, contours, (int)i, color );
// 椭圆,ellipse画椭圆有两种方法,外接矩阵是其中之一
ellipse( drawing, minEllipse[i], color, 2 );
// 取出倾斜矩阵的四个点
Point2f rect_points[4];
minRect[i].points( rect_points );
for ( int j = 0; j < 4; j++ )
{
line( drawing, rect_points[j], rect_points[(j+1)%4], color );
}
}
imshow( "Contours", drawing );
}