opencv面试知识点

一、opencv基础

1、OpenCV中cv::Mat的深拷贝和浅拷贝问题

深拷贝:分配新内存的同时拷贝数据,当被赋值的容器被修改时,原始容器数据不会
改变。
在这里插入图片描述

浅拷贝:仅拷贝数据,当被赋值容器修改时,原始容器数据也会做同样改变。

2、opencv常用数据结构和函数

point(点)、Scalar(颜色)、Size(尺寸)、cvtColor函数(颜色空间转换)

2.1、QImage和Mat之间的转换

相关链接

3、颜色空间 RGB、HSV

相关链接

4、基本图形的绘制

DrawEllipse()、DrawFilledCricle()、DrawPolygon()、DrawLine()

二、opencv(core)

1、图像在内存之中的存储方式

取决于通道数

1、灰度图像

在这里插入图片描述

2、多通道:矩阵的类会包含多个子阵:比如RGB[注意opencv中子列的通道顺序是反过来的是BGR而不是RGB]

在这里插入图片描述

彩色图像转灰度图

相关链接

3、如果内存足够大,可以实现连续存储,因此,图像中的各行就能一行一行的连接起来,形成一个长行。连续存储有助于提升扫描速度,可以用isContinuous()判断是不是连续存储

2、访问图像中的像素及像素取反

相关链接

3、获取图像感兴趣范围(ROI)

方法1:
在这里插入图片描述
方法2:
在这里插入图片描述

4、图像的线性混合

理论公式:
在这里插入图片描述
函数:相关链接

5、分离颜色通道、多通道图像混合

函数:相关链接

6、颜色对比度、亮度调整

相关链接

三、opencv(imgproc)图像处理部分

1、卷积运算原理

在这里插入图片描述
首先是垂直边缘检测,对左边的一个6×6的灰度图像进行卷积运算,中间3×3的即为我们通常说的核或者过滤器。从左边的矩阵左上角开始,利用过滤器在该矩阵上进行计算,对应元素相乘后求和,得到一个数值,例如左上角第一个3×3的矩阵,进行卷积后,得到右边4×4矩阵的第一个元素,即-5,以此类推。
在这里插入图片描述
若以图像的形式进行展示,经过边缘检测后,卷积计算结果如上图所示。假设左边即为原始图像,由于像素分布的原因,左边亮,右边暗,经过卷积后,得到图像中的中间垂直边缘,即原始图像中明暗分割的地方。
转载此处

2、平滑处理

平滑(模糊)操作: 为了减少图片的噪音和伪影,平滑图像与图像模糊处理是相同的含义,平滑处理即是通过操作后,使得图像的像素值与邻域内其他像素值的的变化程度减小在一张图像上,边缘的像素值是变化程度最剧烈的地方,而其他相对平缓。因此,平滑图像最直观的表现是图像的上物体的边缘轮廓变得模糊
滤波目的:1、抽出对象的特征作为图像识别的特征模式。2、消除图像数字化时混入的噪声。

线性滤波和非线性滤波

图像的空域线性滤波和非线性滤波在空域对图像进行滤波处理无非两种情况,线性滤波和非线性滤波。滤波的意思就是对原图像的每个像素周围一定范围内的像素进行运算,运算的范围就称为掩膜或领域。而运算就分两种了,如果运算只是对各像素灰度值进行简单处理(如乘一个权值)最后求和,就称为线性滤波;而如果对像素灰度值的运算比较复杂,而不是最后求和的简单运算,则是非线性滤波;如求一个像素周围3x3范围内最大值、最小值、中值、均值等操作都不是简单的加权,都属于非线性滤波。
转载此处

均值滤波

均值滤波又叫归一化滤波,用输出像素点核窗口内的像素均值代替输出点像素值。可以使用函数cv2.blur() 和cv2.boxFilter() 来完成。

高斯滤波

转载此处
是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。通俗的讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。高斯滤波的具体操作是:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。

加权平均: 打个比方说, 一件事情, 你给它打100分, 你的老板给它打60分, 如果平均, 则是(100+60)/2=80分. 但因为老板说的话分量比你重, 假如老板的权重是2, 你是1, 这时求平均值就是加权平均了, 结果是(1001 + 602)/(1+2)=73.3分, 显然向你的老板那里倾斜了。假如老板权重是3,你的权重是1,结果是(1001+603)/(1+3)=70。这就是根据权重的不同进行的平均数的计算,所以又叫加权平均数。

GaussianBlur(src,dst, ksize, sigmaX[, sigmaY[, borderType]]])

参数:
src:源图像
dst:目标图像
borderType:边缘像素的平滑方式
ksize:滤波器的宽度核高度
sigmax:高斯核在x方向的sigma值(最大值的半宽)
sigmay:在y方向的sigma值

#include<iostream>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
 
using namespace std;
using namespace cv;
 
int main()
{
	Mat img, imgGray,result,dst;
	img = imread("C:\\Users\\shawn\\Pictures\\Saved Pictures\\1.JPG");
	if (!img.data) {
		cout << "Please input image path" << endl;
		return 0;
	}
	imshow("原图", img);
	/*cvtColor(img, imgGray, CV_BGR2GRAY);
	imshow("灰度图", imgGray);
	threshold(imgGray, result, 100, 255, CV_THRESH_BINARY);
	imshow("二值化图", result);//二值化*/
	GaussianBlur(img, dst, Size(7, 7), 0, 0);//平滑操作
	//显示效果图
	imshow("高斯滤波【效果图】", dst);
	waitKey();
	destroyAllWindows();
 
    return 0;
}

3、opencv形态学

相关链接

4、图像尺寸缩放

缩放图片:
cv::resize(image, imgResize, Size(),0.5,0.5);//按0.5倍比例缩放图片 
向上采样:
pyrUp( tmp, dst, Size( tmp.cols*2, tmp.rows*2 );
向下采样:
pyrDown( tmp, dst, Size( tmp.cols/2, tmp.rows/2 )

resize函数默认插值算法为双线性插值算法
插值算法

5、图像阈值处理(图像二值化)

基本阈值操作:

threshold( src_gray, dst, threshold_value, max_BINARY_value,threshold_type );

参数意义: src_gray:输入图,只能输入单通道图像,通常来说为灰度图
dst:输出图
threshold_value:阈值
max_BINARY_value:当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
threshold_type :二值化操作的类型,包括以下5种类型:cv2.THRESH_BINARY;cv2.THRESH_BINARY_INV;cv2.THRESH_TRUNC;cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV。
1、cv2.THRESH_BINARY:超过阈值部分取maxval(最大值),否则取0
2、cv2.THRESH_BINARY_INV:THRESH_BINARY的反转,即超过阈值部分取0,否则取maxval(最大值)
3、cv2.THRESH_TRUNC:大于阈值部分设为阈值,否则不变 4、cv2.THRESH_TOZERO:大于阈值部分不改变,否则设为0
5、cv2.THRESH_TOZERO_INV:THRESH_TOZERO的反转

自适应阈值操作:

#图像自适应阈值二值化函数
adaptiveThreshold(src,dst,maxval, thresh_type, type, Block Size, C);

参数意义:
dst:输出图
src:输入图,只能输入单通道图像,通常来说为灰度图
maxval:当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
thresh_type:阈值的计算方法,包含以下2种类型:cv2.ADAPTIVE_THRESH_MEAN_C:区域内均值;cv2.ADAPTIVE_THRESH_GAUSSIAN_C:区域内像素点加权和,权重为一个高斯窗口
type:二值化操作的类型,与固定阈值函数相同,用于控制参数2 maxval,包含以下5类,见固定阈值函数参数意义
Block Size:图片中区域的大小
C :阈值计算方法中的常数项

四、图像变换

1、边缘检测

参考博主
https://wangsp.blog.csdn.net/article/details/118874145
图像边缘是图像最基本的特征,边缘在图像分析中起着重要的用。边缘检测的目的就是找到图像中亮度变化剧烈的像素点构成的集合,表现出来往往是轮廓。如果图像中边缘能够精确的测量和定位,那么,就意味着实际的物体能够被定位和测量,包括物体的面积、物体的直径、物体的形状等就能被测量

边缘检测的一般步骤为:
1、滤波:边缘检测算法主要是基于图像强度的一阶和二阶导数,但是导数对于噪声很敏感,因此需要采用滤波器来改善与噪声有关的边缘检测器的性能
2、增强:增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将灰度点邻域强度值有显著变化的点凸显出来
3、检测:邻域中有很多的点的梯度值较大,但是在特定的应用中,这些点并不是要找的边缘点,需要取舍

canny 边缘提取

void Canny(
InputArray image,        输入图像(单通道)
OutputArray edges,     输出图像
double threshold1,      阈值T1
double threshold2,     阈值T2 一般为 2*T1
int apertureSize = 3,     Soble算子的size窗口大小 
bool L2gradient = false   默认false L1归一化,Ture则为L2归一化
);

2、霍夫变换

官方教程
1、使用OpenCV的以下函数 HoughLines 和 HoughLinesP 来检测图像中的直线.

HoughLines(dst, lines, 1, CV_PI/180, 100, 0, 0 );

参数:
dst: 边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图)
lines: 储存着检测到的直线的参数对 (r,\theta) 的容器 * rho : 参数极径 r 以像素值为单位的分辨率. 我们使用 1 像素.
theta: 参数极角 \theta 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
threshold: 要”检测” 一条直线所需最少的的曲线交点
srn and stn: 参数默认为0. 查缺OpenCV参考文献来获取更多信息.

HoughLinesP(dst, lines, 1, CV_PI/180, 50, 50, 10 );

参数:
dst: 边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图) * lines: 储存着检测到的直线的参数对 (x_{start}, y_{start}, x_{end}, y_{end}) 的容器
rho : 参数极径 r 以像素值为单位的分辨率. 我们使用 1 像素.
theta: 参数极角 \theta 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
threshold: 要”检测” 一条直线所需最少的的曲线交点 * minLinLength: 能组成一条直线的最少点的数量. 点数量不足的直线将被抛弃.
maxLineGap: 能被认为在一条直线上的亮点的最大距离.

2、使用OpenCV函数 HoughCircles 在图像中检测圆.
官方教程

HoughCircles( src_gray, circles, CV_HOUGH_GRADIENT, 1, src_gray.rows/8, 200, 100, 0, 0 );

参数:
src_gray: 输入图像 (灰度图)
circles: 存储下面三个参数: x_{c}, y_{c}, r 集合的容器来表示每个检测到的圆.
CV_HOUGH_GRADIENT: 指定检测方法. 现在OpenCV中只有霍夫梯度法
dp = 1: 累加器图像的反比分辨率
min_dist = src_gray.rows/8: 检测到圆心之间的最小距离
param_1 = 200: Canny边缘函数的高阈值
param_2 = 100: 圆心检测阈值.
min_radius = 0: 能检测到的最小圆半径, 默认为0.
max_radius = 0: 能检测到的最大圆半径, 默认为0

3、重映射

概念:把一个图像中一个位置的像素放置到另一个图片指定位置的过程.
示例:图像镜像

4、仿射变化

图像仿射变化
透视投影变换

5、直方图均衡化

图像的直方图是什么?

直方图是图像中像素强度分布的图形表达方式.
它统计了每一个强度值所具有的像素个数.
在这里插入图片描述

直方图均衡化是什么?

直方图均衡化是通过拉伸像素强度分布范围来增强图像对比度的一种方法.
说得更清楚一些, 以上面的直方图为例, 你可以看到像素主要集中在中间的一些强度值上. 直方图均衡化要做的就是 拉伸 这个范围. 见下面左图: 绿圈圈出了 少有像素分布其上的 强度值. 对其应用均衡化后, 得到了中间图所示的直方图. 均衡化的图像见下右图.
在这里插入图片描述

equalizeHist( src, dst );

五、图像轮廓与图像分割修复

1、查找并绘制轮廓

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>

using namespace cv;
using namespace std;

Mat src; Mat src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);

/// Function header
void thresh_callback(int, void* );

/** @function main */
int main( )
{
  /// 加载源图像
  src = imread("F:/projects/opencv/opencv1.jpeg", 1 );

  /// 转成灰度并模糊化降噪
  cvtColor( src, src_gray, COLOR_BGR2GRAY );
  blur( src_gray, src_gray, Size(3,3) );

  /// 创建窗体
  string source_window = "Source";
  namedWindow( source_window, WINDOW_AUTOSIZE );
  imshow( source_window, src );

  createTrackbar( " Canny thresh:", //滚动条名字
                  "Source",         //指定窗口
                  &thresh,          //设置滑块初始值位置
                  max_thresh,       //用来指定滚动条可以滚动的最大值
                  thresh_callback );//回调函数
  thresh_callback( 0, 0 );

  waitKey(0);
  return(0);
}

/** @function thresh_callback */
void thresh_callback(int, void* )
{
  Mat canny_output;
  vector<vector<Point> > contours;
  vector<Vec4i> hierarchy;

  /// 用Canny算子检测边缘
  Canny( src_gray, canny_output, thresh, thresh*2, 3 );
  /// 寻找轮廓
  findContours( canny_output, //输入图像
                contours,     //全部发现的轮廓对象
                hierarchy,    //拓扑结构图
                RETR_TREE,
                CHAIN_APPROX_SIMPLE,
                Point(0, 0) );

  /// 绘出轮廓
  Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
  for( int i = 0; i< contours.size(); i++ )
     {
       Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
       drawContours( drawing,   //输出图像
                     contours,  //全部发现的轮廓对象
                     i,         //轮廓索引号
                     color,     //绘制颜色
                     2,         //绘制线宽
                     8,         //线的类型LINE_8
                     hierarchy, //拓扑结构图
                     0,
                     Point() );
     }

  /// 在窗体中显示结果
  namedWindow( "Contours", WINDOW_AUTOSIZE );
  imshow( "Contours", drawing );
}

2、寻找图像凸包

#include "opencv2/highgui/highgui.hpp"
 #include "opencv2/imgproc/imgproc.hpp"
 #include <iostream>
 #include <stdio.h>
 #include <stdlib.h>

 using namespace cv;
 using namespace std;

 Mat src; Mat src_gray;
 int thresh = 100;
 int max_thresh = 255;
 RNG rng(12345);

 /// Function header
 void thresh_callback(int, void* );

/** @function main */
int main( int argc, char** argv )
 {
   /// 加载源图像
   src = imread( argv[1], 1 );

   /// 转成灰度图并进行模糊降噪
   cvtColor( src, src_gray, CV_BGR2GRAY );
   blur( src_gray, src_gray, Size(3,3) );

   /// 创建窗体
   char* source_window = "Source";
   namedWindow( source_window, CV_WINDOW_AUTOSIZE );
   imshow( source_window, src );

   createTrackbar( " Threshold:", "Source", &thresh, max_thresh, thresh_callback );
   thresh_callback( 0, 0 );

   waitKey(0);
   return(0);
 }

 /** @function thresh_callback */
 void thresh_callback(int, void* )
 {
   Mat src_copy = src.clone();
   Mat threshold_output;
   vector<vector<Point> > contours;
   vector<Vec4i> hierarchy;

   /// 对图像进行二值化
   threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY );

   /// 寻找轮廓
   findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, 
    Point(0, 0) );

   /// 对每个轮廓计算其凸包
   vector<vector<Point> >hull( contours.size() );
   for( int i = 0; i < contours.size(); i++ )
      {  convexHull( Mat(contours[i]), hull[i], false ); }

   /// 绘出轮廓及其凸包
   Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
   for( int i = 0; i< contours.size(); i++ )
      {
        Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
        drawContours( drawing, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
        drawContours( drawing, hull, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
      }

   /// 把结果显示在窗体
   namedWindow( "Hull demo", CV_WINDOW_AUTOSIZE );
   imshow( "Hull demo", drawing );
 }

3、Grabcut

转载此文
Graph cuts是一种十分有用和流行的能量优化算法,在计算机视觉领域普遍应用于前背景分割(Image segmentation)、立体视觉(stereo vision)、抠图(Image matting)等。此类方法把图像分割问题与图的最小割(min cut)问题相关联。

OpenCV中的GrabCut该算法利用了图像中的纹理(颜色)信息和边界(反差)信息,只要少量的用户交互操作即可得到比较好的分割结果.

#include<opencv2\opencv.hpp>
using namespace cv;
void onMouse(int event, int x, int y, int flags, void* userdata);
Rect rect;
Mat src, roiImg, result;
void showImg();
int main(int arc, char** argv) {
    src = imread("F:/projects/opencv/girl.png");
    namedWindow("input", WINDOW_AUTOSIZE);
    imshow("input", src);
    setMouseCallback("input", onMouse);
    //定义输出结果,结果为:GC_BGD =0(背景),GC_FGD =1(前景),GC_PR_BGD = 2
    //(可能的背景), GC_PR_FGD = 3(可能的前景)
    Mat result = Mat::zeros(src.size(), CV_8UC1);
    // GrabCut 抠图
    //两个临时矩阵变量,作为算法的中间变量使用
    Mat bgModel, fgModel;
    char c = waitKey(0);
    if (c == 'g') {
        grabCut(src, result, rect, bgModel, fgModel, 1, GC_INIT_WITH_RECT);
        //比较result的值为可能的前景像素才输出到result中(比较输入的src1和src2中的元素,输出结果到
        //dst中)
        compare(result,         //src1
                GC_PR_FGD,      //src2
                result,         //dst
                CMP_EQ);        //等于
        // 产生输出图像
        Mat foreground(src.size(), CV_8UC3, Scalar(255, 255, 255));
        //将原图像src中的result区域拷贝到foreground中
        src.copyTo(foreground, result);
        namedWindow("output", WINDOW_AUTOSIZE);
        imshow("output", foreground);
        waitKey(0);
    }
    waitKey(0);
    return 0;
}


void showImg() {
    src.copyTo(roiImg);
    rectangle(roiImg, rect, Scalar(0, 0, 255), 2);
    imshow("input", roiImg);
}
//鼠标选择矩形框
void onMouse(int event, int x, int y, int flags, void* userdata) {
    switch (event)
    {
    case EVENT_LBUTTONDOWN://鼠标左键按下事件
        rect.x = x;
        rect.y = y;
        rect.width = 1;
        rect.height = 1;
        break;
    case EVENT_MOUSEMOVE://鼠标移动事件
        if (flags && EVENT_FLAG_LBUTTON) {
            rect = Rect(Point(rect.x, rect.y), Point(x, y));
            showImg();
        }
        break;
    case EVENT_LBUTTONUP://鼠标弹起事件
        if (rect.width > 1 && rect.height > 1) {
            showImg();
        }
        break;
    default:
        break;
    }
}

六、特征提取算法

1、surf特征提取算法(加速鲁棒特征)

Speeded Up Robust Features(SURF,加速稳健特征),是一种稳健的局部特征点检测和描述算法

运用surf算法实现图像拼接

原文链接

步骤:
1、导入目标图片
2、特征点提取和匹配
3、图像配准(利用透射转换)
4、图像拷贝
5、图像融合

关键函数:
detectAndCompute:寻找特征点

void detectAndCompute(
InputArray image, //图像
InputArray mask, //掩模
CV_OUT std::vector& keypoints,//输出关键点的集合
OutputArray descriptors,//计算描述符(descriptors[i]是为keypoints[i]的计算描述符)
bool useProvidedKeypoints=false //使用提供的关键点
);

drawMatches:匹配的特征点连成线

drawMatches(left, //源图像1
key2,           //源图像1的特征点
right,          //源图像2
key1,           //源图像2的特征点
good_matches,   //源图像1的特征点匹配源图像2的特征点
outimg,         //输出图像
Scalar::all(-1),//匹配的颜色(特征点和连线),若matchColor==Scalar::all(-1),颜色随机.
Scalar::all(-1),//单个点的颜色,即未配对的特征点,若matchColor==Scalar::all(-1),颜色随机
vector<char>(), //Mask决定哪些点将被画出,若为空,则画出所有匹配点
DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);//不绘制单独的特征点

findHomography

代码实现:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;

typedef struct
{
    //四个顶点
    Point2f left_top;
    Point2f left_bottom;
    Point2f right_top;
    Point2f right_bottom;
}four_corners_t;

four_corners_t corners;

//计算配准图的四个顶点坐标
void CalcCorners(const Mat& H, const Mat& src)
{
    double v2[] = { 0, 0, 1 };//左上角
    double v1[3];//变换后的坐标值
    Mat V2 = Mat(3, 1, CV_64FC1, v2);  //列向量
    Mat V1 = Mat(3, 1, CV_64FC1, v1);  //列向量

    V1 = H * V2;
    //左上角(0,0,1)
    cout << "V2: " << V2 << endl;
    cout << "V1: " << V1 << endl;
    corners.left_top.x = v1[0] / v1[2];
    corners.left_top.y = v1[1] / v1[2];

    //左下角(0,src.rows,1)
    v2[0] = 0;
    v2[1] = src.rows;
    v2[2] = 1;
    V2 = Mat(3, 1, CV_64FC1, v2);  //列向量
    V1 = Mat(3, 1, CV_64FC1, v1);  //列向量
    V1 = H * V2;
    corners.left_bottom.x = v1[0] / v1[2];
    corners.left_bottom.y = v1[1] / v1[2];

    //右上角(src.cols,0,1)
    v2[0] = src.cols;
    v2[1] = 0;
    v2[2] = 1;
    V2 = Mat(3, 1, CV_64FC1, v2);  //列向量
    V1 = Mat(3, 1, CV_64FC1, v1);  //列向量
    V1 = H * V2;
    corners.right_top.x = v1[0] / v1[2];
    corners.right_top.y = v1[1] / v1[2];

    //右下角(src.cols,src.rows,1)
    v2[0] = src.cols;
    v2[1] = src.rows;
    v2[2] = 1;
    V2 = Mat(3, 1, CV_64FC1, v2);  //列向量
    V1 = Mat(3, 1, CV_64FC1, v1);  //列向量
    V1 = H * V2;
    corners.right_bottom.x = v1[0] / v1[2];
    corners.right_bottom.y = v1[1] / v1[2];

}

///5、图像融合

//优化两图的连接处,使得拼接自然
void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst)
{
    int start = MIN(corners.left_top.x, corners.left_bottom.x);//开始位置,即重叠区域的左边界

    double processWidth = img1.cols - start;//重叠区域的宽度
    int rows = dst.rows;
    int cols = img1.cols; //注意,是列数*通道数
    double alpha = 1;//img1中像素的权重
    for (int i = 0; i < rows; i++)
    {
        uchar* p = img1.ptr<uchar>(i);  //获取第i行的首地址
        uchar* t = trans.ptr<uchar>(i);
        uchar* d = dst.ptr<uchar>(i);
        for (int j = start; j < cols; j++)
        {
            //如果遇到图像trans中无像素的黑点,则完全拷贝img1中的数据
            if (t[j * 3] == 0 && t[j * 3 + 1] == 0 && t[j * 3 + 2] == 0)
            {
                alpha = 1;
            }
            else
            {
                //img1中像素的权重,与当前处理点距重叠区域左边界的距离成正比,实验证明,这种方法确实好
                alpha = (processWidth - (j - start)) / processWidth;
            }

            d[j * 3] = p[j * 3] * alpha + t[j * 3] * (1 - alpha);
            d[j * 3 + 1] = p[j * 3 + 1] * alpha + t[j * 3 + 1] * (1 - alpha);
            d[j * 3 + 2] = p[j * 3 + 2] * alpha + t[j * 3 + 2] * (1 - alpha);

        }
    }

}

int main(int argc, char *argv[])
{
    Mat left=imread("F:/projects/opencv/left.png");//左侧:图片路径
    Mat right=imread("F:/projects/opencv/right.png");//右侧:图片路径

    imshow("left",left);
    imshow("right",right);

///1、导入目标图片

    //创建SURF对象
    Ptr<SURF>surf;   //可以容纳800个特征点
    surf = SURF::create(800);//参数 查找的海森矩阵 create 海森矩阵阀值

    //暴力匹配器
    BFMatcher matcher;

    vector<KeyPoint>key1,key2;
    Mat c,d;

///2、特征点提取和匹配 

    //寻找特征点
    surf->detectAndCompute(left,Mat(),key2,d);
    surf->detectAndCompute(right,Mat(),key1,c);

    //特征点对比,保存下来
    vector<DMatch>matches;//DMatch 点和点之间的关系
    //使用暴力匹配器匹配特征点,找到存来
    matcher.match(d,c,matches);

    //排序 从小到大
    sort(matches.begin(),matches.end());    

    //保留最优的特征点对象
    vector<DMatch>good_matches;//最优

    //设置比例
    int ptrPoint = std::min(50,(int)(matches.size()*0.15));

    for(int i = 0;i < ptrPoint;i++)
    {
        good_matches.push_back(matches[i]);
    }

    //最佳匹配的特征点连成线
    Mat outimg;

    drawMatches(left,           //源图像1
                key2,           //源图像1的特征点
                right,          //源图像2
                key1,           //源图像2的特征点
                good_matches,   //源图像1的特征点匹配源图像2的特征点
                outimg,         //输出图像
                Scalar::all(-1),//匹配的颜色(特征点和连线),若matchColor==Scalar::all(-1),颜色随机.
                Scalar::all(-1),//单个点的颜色,即未配对的特征点,若matchColor==Scalar::all(-1),颜色随机
                vector<char>(), //Mask决定哪些点将被画出,若为空,则画出所有匹配点
                DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);//不绘制单独的特征点

    imshow("outimg",outimg);

///3、图像配准
    
    //特征点配准
    vector<Point2f>imagepoint1,imagepoint2;

    for(int i = 0;i<good_matches.size();i++)
    {
        imagepoint1.push_back(key1[good_matches[i].trainIdx].pt);
        imagepoint2.push_back(key2[good_matches[i].queryIdx].pt);
    }

    //透视转换
    Mat homo = findHomography(imagepoint1,imagepoint2,RANSAC);

    imshow("homo",homo);

    //四个顶点坐标的转换计算
    CalcCorners(homo,right);

    Mat imageTranForm;
    warpPerspective(right,imageTranForm,homo,
                    Size(MAX(corners.right_top.x,
                             corners.right_bottom.x),
                         left.rows));

    imshow("imageTranForm",imageTranForm);

///4、图像拷贝
    
    //创建拼接后的图,计算图的大小
    int dst_width = imageTranForm.cols;//获取最右点为拼接图长度
    int dst_height = left.rows;

    Mat dst(dst_height,dst_width,CV_8UC3);
    dst.setTo(0);

    imageTranForm.copyTo(dst(Rect(0,0,imageTranForm.cols,imageTranForm.rows)));
    left.copyTo(dst(Rect(0,0,left.cols,left.rows)));

    //优化拼接,主要目的去除黑边
    OptimizeSeam(left,imageTranForm, dst);

    imshow("dst",dst);

    waitKey(0);

    return 0;
}

七、简单示例

1、竹子替换:

在这里插入图片描述

步骤:
1、提取绿色区域(利用inRange进行图像二值化操作)
2、图像非操作,创建掩膜
3、将原图与掩膜进行与运算后覆盖到红底矩阵,

void inrange_Demo(Mat& image){

    Mat hsv, mask;
    cvtColor(image, hsv, COLOR_BGR2HSV);
    inRange(hsv, Scalar(35, 43, 46), Scalar(77, 255, 255), mask);//将在两个阈值内的像素值设置为白色(255),而不在阈值区间内的像素值设置为黑色(0)
    imshow("mask", mask);

    Mat redback = Mat::zeros(image.size(), image.type());
    redback = Scalar(40, 40, 200);
    bitwise_not(mask, mask);//图像非操作 ~1=0,~0=1
    imshow("~mask", mask);

    imshow("image", image);

    image.copyTo(redback, mask);//原图(image)与掩膜(mask)进行与运算后覆盖到目标图(reback)
    imshow("redback", redback);
}

2、提取表格线

在这里插入图片描述

步骤:
1、对原图进行置灰、平滑处理、二值化后,得知轮廓鲜明的灰度图
2、设置水平线内核后,进行腐蚀+膨胀操作,获取只保留水平线图
3、设置竖直线内核后,进行开操作操作,获取只保留竖直线图
4、对水平线图和竖直线图,进行或操作

void extract_line_Demo(Mat& image) {
    int height = image.rows;
    int width = image.cols;
    Mat gray_img, binary_img;
    Mat horiz_img, vert_img;

    cvtColor(image, gray_img, COLOR_BGR2GRAY);//原图转灰度图
    bitwise_not(gray_img, gray_img);//图像非操作,~1=0,~0=1(将二进制图片的效果反转既黑色变白色,白色变黑色)
    imshow("image", gray_img);
    GaussianBlur(gray_img, gray_img, Size(3, 3), 10);//高斯模糊
    adaptiveThreshold(gray_img, binary_img, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 15, -2);//自适应阈值操作,凸显目标的轮廓

    int horiz_size = (int)(width / 15);  // horiz_size值越大,检测出长线条,筛选掉段线条
    int vert_size = (int)(height / 15);  // 同上

    Mat horiz = getStructuringElement(MORPH_RECT, Size(horiz_size, 1), Point(-1, -1));//设置水平线内核
    erode(binary_img, horiz_img, horiz);
    dilate(horiz_img, horiz_img, horiz);
    imshow("horiz_img", horiz_img);

    Mat vert = getStructuringElement(MORPH_RECT, Size(1, vert_size), Point(-1, -1));
    morphologyEx(binary_img, vert_img, MORPH_OPEN, vert, Point(-1, -1)); //开操作=腐蚀+膨胀
    imshow("vert_img", vert_img);

    Mat net_img,mask_img;
    bitwise_or(horiz_img, vert_img, mask_img);//图像或操作,1|1=1,1|0=1,0|1=1,0|0=0
    imshow("mask_img", mask_img);
}
  • 4
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值