opencv面试知识点

2023-05-16

文章目录

  • 一、opencv基础
    • 1、OpenCV中cv::Mat的深拷贝和浅拷贝问题
    • 2、opencv常用数据结构和函数
      • 2.1、QImage和Mat之间的转换
    • 3、颜色空间 RGB、HSV
    • 4、基本图形的绘制
  • 二、opencv(core)
    • 1、图像在内存之中的存储方式
      • 彩色图像转灰度图
    • 2、访问图像中的像素及像素取反
    • 3、获取图像感兴趣范围(ROI)
    • 4、图像的线性混合
    • 5、分离颜色通道、多通道图像混合
    • 6、颜色对比度、亮度调整
  • 三、opencv(imgproc)图像处理部分
    • 1、卷积运算原理
    • 2、平滑处理
    • 线性滤波和非线性滤波
      • 均值滤波
      • 高斯滤波
    • 3、opencv形态学
    • 4、图像尺寸缩放
    • 5、图像阈值处理(图像二值化)
  • 四、图像变换
    • 1、边缘检测
      • canny 边缘提取
    • 2、霍夫变换
    • 3、重映射
    • 4、仿射变化
    • 5、直方图均衡化
      • 图像的直方图是什么?
      • 直方图均衡化是什么?
  • 五、图像轮廓与图像分割修复
    • 1、查找并绘制轮廓
    • 2、寻找图像凸包
    • 3、Grabcut
  • 六、特征提取算法
    • 1、surf特征提取算法(加速鲁棒特征)
      • 运用surf算法实现图像拼接
  • 七、简单示例
    • 1、竹子替换:
    • 2、提取表格线

一、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);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

opencv面试知识点 的相关文章

  • 将 vec3b 转换为 mat

    我有彩色图像im 我想使用以下代码使用 vec3b 获取 3 通道图像的像素值 for int i 0 i lt im rows i for int j 0 j lt im cols j for int k 0 k lt nChannels
  • 在 Python 中将视频源从 Javascript 传递到 OpenCV

    我正在尝试创建一个网络应用程序来检测实时视频源中的面孔 我已经用 Javascript 编写了网络摄像头源代码 因为我想稍后托管该应用程序 使用 Javascript 获取 feed 的代码 var video document query
  • pytesseract 无法按预期识别文本?

    我正在尝试通过 opencv 和 pytesseract 运行一个简单的车牌图像来获取文本 但我无法从中获取任何内容 按照此处的教程进行操作 https Circuitdigest com microcontroller projects
  • Python OpenCV cv.WaitKey 在 Ubuntu 模 256 映射上正确返回奇怪的输出

    我正在使用 OpenCV 2 2 运行 Ubuntu 11 10 Lenovo T400 我相信导入是通过 import cv2 cv as cv 完成的 如果我只是 导入简历 也会发生这个问题 我最近开始遇到这个问题 这有点奇怪 我不知道
  • 如何在Python中删除图像的背景

    我有一个包含全角人类图像的数据集 我想删除这些图像中的所有背景 只留下全角人物 我的问题 有没有Python代码可以做到这一点 我是否需要每次都指定人员对象的坐标 这是使用 Python OpenCV 的一种方法 读取输入 转换为灰色 阈值
  • C++/OpenCV - 用于视频稳定的卡尔曼滤波器

    我尝试使用卡尔曼滤波器稳定视频以进行平滑 但我有一些问题 每次 我都有两帧 一帧是当前帧 另一帧是当前帧 这是我的工作流程 计算 goodFeaturesToTrack 使用 calcOpticalFlowPyrLK 计算光流 只保留优点
  • 使用python opencv从zip加载图像

    我能够成功地从 zip 加载图像 with zipfile ZipFile test zip r as zfile data zfile read test jpg how to open this using imread or imde
  • 我无法在 docker 中安装 opencv-contrib-python

    我尝试安装opencv contrib python但我无法让它在 docker 上工作 它说找不到满足 opencv contrib python 要求的版本 I tried pip install opencv contrib pyth
  • OpenCV 的 findHomography 产生无意义的结果

    我正在制作一个程序 使用 OpenCV 2 43 中的 ORB 跟踪功能 我遵循并使用了建议从这里 https stackoverflow com questions 9919505 how can i extract fast featu
  • Python OpenCV:检测大体运动方向?

    我仍在编写一个书籍扫描脚本 现在 我所需要的只是能够自动检测翻页 这本书占据了 90 的屏幕 我使用一个粗糙的网络摄像头进行运动检测 所以当我翻页时 运动方向基本上是同一个方向 我修改了一个运动跟踪脚本 但导数却无济于事 usr bin e
  • Matlab 中 interp2 的类似 OpenCV Api

    有没有类似的功能 其工作原理与 interp2 x y frame z xd yd linear 0 在 OpenCV 中 功能cv remap 几乎可以满足您的要求 请参阅文档here http docs opencv org modul
  • 使用 cmake 和 opencv 对符号“gzclose”的未定义引用[关闭]

    Closed 这个问题是无法重现或由拼写错误引起 help closed questions 目前不接受答案 我尝试构建该项目 doppia https bitbucket org rodrigob doppia 但发生链接错误 我想这是一
  • 使用 OpenCV 裁剪黑色边缘

    我认为这应该是一个很简单的问题 但我找不到解决方案或有效的关键字进行搜索 我只有这个图像 黑边没有用 所以我想把它们剪掉 只留下 Windows 图标 和蓝色背景 我不想计算Windows图标的坐标和大小 GIMP 和 Photoshop
  • 如何使用 OpenCV 找到红色区域? [复制]

    这个问题在这里已经有答案了 我正在尝试编写一个检测红色的程序 然而有时它比平常更暗 所以我不能只使用一个值 检测不同深浅的红色的最佳范围是多少 我目前使用的范围是 128 0 0 255 60 60 但有时它甚至检测不到我放在它前面的红色物
  • 编译使用Basler相机的程序

    我正在尝试使用 Basler 相机捕获图像的 C 程序来工作 我拿到 来自制造商的代码 它应该 非常容易使用 但是 链接它有 成为一场噩梦 我的 C 时代已经过去了 最近只使用 Matlab 所以我可能会犯一些愚蠢的错误 但请赐教 代码如下
  • opencv如何使用compareHist函数

    img cv2 imread mandrill png histg cv2 calcHist img 0 None 256 0 256 if len sys argv lt 2 print gt gt sys stderr Usage sy
  • 如何使用requirements.txt 在 Heroku python Web 应用程序中安装 Dlib?

    我构建了一个涉及机器学习的 Python Flask Web API 但在 Heroku 上部署它时遇到了很多挫折 问题是 我的应用程序依赖于 Dlib 一个库 我似乎找不到在我的 Heroku 服务器中安装的方法 我正在试图解决这个问题
  • bitblt 在 Windows 10 版本 1703 上失败 (15063.138)

    使用 Visual Studio 2017 vc141 以下代码应该从前游戏窗口获取屏幕截图 但现在它返回黑色和空白图像 唯一的游戏问题 尝试过 OpenGL 和 Vulkan ogl 返回黑色 vulkan 返回白色 在升级到 Windo
  • 如何使用 OpenCV 检测图像帧中的对象?

    我正在使用 Raspberry Pi 开发一个漫游器 它将清扫房间并捡起掉落在地上的物体 为了检测物体 我使用了在流动站操作开始时拍摄的参考图像 以及每 10 秒单击一次的图像 新图像 为了确定图像帧是否发生变化 我在参考图像和新图像之间进
  • brew 链接 jpeg 问题

    我正在尝试安装opencv在 Mac OSX Lion 上 brew install opencv 我收到以下错误 以及其他一些类似的错误 Error The linking step did not complete successful

随机推荐

  • colmap pthread 错误

    Looking for include file pthread h Looking for include file pthread h found Looking for pthread create Looking for pthre
  • 全球系留无人机系统行业调研及趋势分析报告

    本文调研和分析全球系留无人机系统发展现状及未来趋势 xff0c 核心内容如下 xff1a xff08 1 xff09 全球市场总体规模 xff0c 分别按销量和按收入进行了统计分析 xff0c 历史数据2017 2021年 xff0c 预测
  • 安装arm-none-eabi版本添加环境变了还是出错问题解决

    安装arm none eabi版本添加环境变了还是出错问题解决 问题解决 问题 已经在 profile文件里面添加了如下命令 span class token keyword export span span class token con
  • 基于ubuntu16.04 塔建PX4编译环境

    基于ubuntu16 04 塔建PX4编译环境 基于ubuntu16 04 塔建PX4编译环境环境塔建官网网址 xff1a 权限设定删除modemmanager更新软件包列表 xff0c 并为所有PX4构建目标安装以下依赖项 安装pyulo
  • APM编译记录-基于OMNIBUSF4-V3飞控板

    APM编译记录 基于OMNIBUSF4 V3飞控板 APM编译记录Bootloader问题固件编译 APM编译记录 初次使用APM xff0c 根据博客文章以及结合官方教程摸索 xff0c 发现和PX4还是有挺大差别的 xff0c 目前系统
  • ubuntu16.04基于eclipse搭建px4编译环境+Jlink调式

    ubuntu16 04基于eclipse搭建px4编译环境 43 Jlink调式 xff09 ubuntu16 04基于eclipse搭建px4编译环境 43 Jlink调式一 工具获取1 eclipse2 jlink3 jdk4 arm
  • Ardupilot通过mavlink + 4way_protocol对BLHeli_s电调的读写

    Ardupilot通过mavlink 43 4way protocol对BLHeli s电调的读写 前言上位机1 Mission Planner2 BLHeliSuite3 BLHeliSuite32 Ardupilot 源码编译1 启动B
  • C/C++中 float与uint16类型转换方法

    C C 43 43 中 float与uint16类型转换方法 为什么要做float与uint16互相转换方法一 xff1a 方法二 xff1a 为什么要做float与uint16互相转换 此需求在串口通信时常常会被用到 xff0c 串口只能
  • Keil V5仿真出现*** error 65: access violation at 0x40021000 : no ‘read‘ permission 解决办法

    Keil V5仿真出现 error 65 access violation at 0x40021000 no 39 read 39 permission 解决办法 问题解决办法1 进入debug的map设置地址2 新建debug ini配置
  • Ardupilot-NMEA协议的GNSS处理优化

    Ardupilot NMEA协议的GNSS处理优化 Ardupilot NMEA协议的GNSS处理优化原固件存在的问题解决办法 Ardupilot NMEA协议的GNSS处理优化 原固件存在的问题 1 当所使用的GNSS模块数据反馈频率不到
  • FMT-模型在环仿真(MIL)

    前提条件 matlab版本是2018B及以上版本下载好了FMT Model工程 设置MIL仿真 打开matlab xff0c 进入到FMT Model目录 xff0c 左键双击打开FMT Model prj 打开过程它自己会初始化工程 xf
  • 【移植Ardupilot的日志记录方法到linux上】

    移植Ardupilot的日志记录方法到linux上 说明日志结构组成日志写入操作预定义日志项运行时添加日志项的方法 单例测试编译方法查看数据其他 说明 采用二进制文件记录 xff0c 可在mission planer查看 支持所有数据类型记
  • 一、linux内核源码分析(内核源码结构组成)

    应该说是把linux内核这部分知识提前了 xff0c 因为坐公交需要点视频来打发一下时间 xff0c 感觉linux内核的视频就不错 xff0c 就顺便把linux内核提前了 这一次也是打算视频和书一起看 xff0c 然后总结 书的话还是推
  • Android开发:使用Lambda表达式高效开发

    简介 xff1a JDK1 8后java开始支持Lambda表达式 xff0c 使用Lambda表达式我们就可以在Android中替换匿名类的写法 本来一个Button点击事件监听要写6行代码 xff0c 我们1行就可以搞定 1 引入依赖
  • ros开发与室内定位(一)

    ROS下搭建 UWB 下行数据解析驱动 Linux内核版本 xff1a ubuntu 15 05 ROS版本 xff1a indigo 搭建局域网 xff0c 向网内的主机提供室内标签实时的定位信息 xff0c 该Demo可以利用UDP协议
  • ubuntu1604 ROS下实时双目ORB-SLAM的环境搭建

    本文主要记录本萌新从零开始搭建ORB SLAM环境的一个过程 xff0c 因为第一次装双系统 xff0c 本来就遇到了很多的问题 来来回回装了好多天才弄好 xff0c 中间遇到的主要问题和步骤参考放在下边 xff0c 可能帮助到别人的话就倍
  • 机器学习题目汇总

    1 深度学习中的激活函数需要具有哪些属性 xff1f 计算简单非线性具有饱和区几乎处处可微 ABD 解析 xff1a xff08 1 xff09 非线性 xff1a 导数不能是常数 xff08 2 xff09 几乎处处可微 xff1a si
  • 天猫精灵云云对接

    天猫精灵云云对接 一 技能开发二 产品开发 接入过程出现了一些问题 xff0c 特此记录 xff0c 用来给朋友们参考 xff0c 随意记录 xff0c 不接受批评 前情提要 xff1a 注册并认证 天猫精灵开放平台 AliGenie 一
  • 关于Altium Designe中元器件添加PDF的简述

    一 缘由 xff1a 今天无意之间在书本上看到关于元器件的Datasheet添加 xff0c 也想起自己曾经为了查询元器件datasheet xff0c 费了不少时间 xff0c 所以写下了此篇博文 二 关于添加步骤 xff1a 1 打开自
  • opencv面试知识点

    文章目录 一 opencv基础1 OpenCV中cv Mat的深拷贝和浅拷贝问题2 opencv常用数据结构和函数2 1 QImage和Mat之间的转换 3 颜色空间 RGB HSV4 基本图形的绘制 二 opencv xff08 core