利用remap函数实现图像翻转

一个简单的例子

#include<opencv2/core.hpp>
#include<opencv2/imgcodecs.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc.hpp>
#include<iostream>

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<img.rows; i++)
    {
        for(int j=0; j<img.cols; j++)
        {
                //做上下颠倒 故水平坐标不变 竖直坐标翻转
            map_y.at<float>(i, j) = (float)(img.rows - i);
            map_x.at<float>(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;
}

Affine Transformations 仿射变换

仿射变换原理

仿射变换可以实现图像变换,旋转,尺度变换等,其中仿射矩阵有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 );// 对比两幅图片的直方图相似程度

back projection 反向投影

原理链接
反向投影:首先计算某一特征的直方图模式,但存在新的图像时使用模型寻找图像中符合该特征的地方

几个关键函数

//计算直方图函数
 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<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgcodecs.hpp>
#include<opencv2/imgproc.hpp>
#include<iostream>

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<float>(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<vector<Point> > contours;
    // 返回不同轮廓的层次关系,与寻找轮廓的方法有关,非必须
    vector<Vec4i> 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 );
}

寻找图像的凸包(Convex Hull)

简单的理解凸包就是寻找一个包含所有点的橡皮圈

void thresh_callback(int, void* )
{
    Mat canny_output;
    Canny( src_gray, canny_output, thresh, thresh*2 );

    // 这里求轮廓没有考虑层次之间的关系
    vector<vector<Point> > contours;
    findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );

    // 利用convexHull求出所有在轮廓上的点凸包 hull相当于存储凸包各个点
    // convexHull默认的两个参数 输出凸包方向 bool    clockwise = false,  返回点or指针 bool    returnPoints = true
    vector<vector<Point> >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 );
}

Creating Bounding boxes and circles for contours(即包含轮廓的最小矩形和圆)

void thresh_callback(int, void* )
{
    Mat canny_output;
    Canny( src_gray, canny_output, thresh, thresh*2 );

   //不需要层次关系的轮廓
    vector<vector<Point> > contours;
    findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );

   // 多边形的顶点,对每一个轮廓都生成一个多边形
    vector<vector<Point> > contours_poly( contours.size() );
    vector<Rect> boundRect( contours.size() );
    vector<Point2f>centers( contours.size() );
    vector<float>radius( 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<vector<Point> > contours;
    findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );

    vector<RotatedRect> minRect( contours.size() );
    vector<RotatedRect> 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 );
}
10-03 20:48