OpenCV常见库函数(下):轮廓、变换、video类、输入输出

寻找/画出轮廓

floodFill( )

漫水法,常用于寻找轮廓的预处理操作,和“画图”软件中“油漆桶”工具有相同的效果
image
image
seedPoint 起始像素点
newVal 重绘像素区域的新的填充值(颜色)
rect 设置floodFill函数将要重绘区域的最小边界矩形区域
loDiff 当前选定像素与其连通区中相邻像素中的一个像素,或者与加入该连通区的一个seedPoint像素,二者之间的最大下行差异值。
upDiff 当前选定像素与其连通区中相邻像素中的一个像素,或者与加入该连通区的一个seedPoint像素,二者之间的最大上行差异值。
flags 操作标志符,此参数包含三个部分
image
eg

cv::floodFill(src, Point(50,300), Scalar(155, 255,55), &ccomp, Scalar(20, 20, 20),Scalar(20, 20, 20));

OpenCV中,轮廓是由STL风格的vector<>模板对象表示的,其中vector中的每个元素都编码了曲线上,下一点的位置信息。

findContour()

寻找二值图中的轮廓,并保存为一组点,算法类似于漫水法,遍历所有像素并查找相邻像素

void findContours(InputOutputArray image,OutputArrayOfArrays contours,int mode,int method,Point offset = Point());

contours:std::vector<std::vector<cv::Point>> contours,是一个向量,并且是一个双重向量,向量内每个元素保存了一组由连续的Point点构成的点的集合的向量,每一组Point点集就是一个轮廓。有多少轮廓,向量contours就有多少元素。

mode:int类型,定义轮廓的检索模式
CV_RETR_EXTERNAL 只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略;
CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1
CV_RETR_CCOMP 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层;
CV_RETR_TREE 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。

method:int类型,定义轮廓的近似方法
CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内
CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留;
CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

eg

//获取轮廓点
std::vector<std::vector<cv::Point>> contours;
cv::findContours(binary_img,contours,cv::RETR_EXTERNAL,cv::CHAIN_APPROX_NONE);

drawContour():根据一组点画出轮廓


void cv::drawContours(InputOutputArray image,InputArrayOfArrays contours,int contourIdx,const Scalar &color,int thickness = 1,int lineType = LINE_8,InputArray hierarchy = noArray(),int maxLevel = INT_MAX,Point offset = Point())

contourIdx:轮廓索引 i,i > 0时,i是几就画 i 所对应的轮廓,如果 i < 0 就画出所有轮廓
color:轮廓线的颜色 用Scalar()存
thickness:轮廓线厚度,如果这个值为负值(例如:thickness=-1),则填充轮廓内部
lineType:轮廓线的连接方式,参见线条连接样式列表

hierarchy:关于层次结构的可选信息。仅当您只想绘制部分轮廓时,才需要此选项
maxLevel: 绘制轮廓的最大级别。如果为0,则仅绘制指定的轮廓。如果为1,函数将绘制该轮廓和所有嵌套轮廓。如果为2,则函数绘制轮廓、所有嵌套轮廓、所有的嵌套到嵌套轮廓等。此参数仅在有可用hierarchy时考虑
offset:可选轮廓偏移参数。将所有绘制的轮廓移动指定的偏移量=(dx,dy)

关于hierarchy 层级容器

image
contours 边界容器

将轮廓点转换为更容易处理的形状对象

minAreaRect(InputArray points)
//通过轮廓点,拟合出最小面积的RotatedRect
//返回值是RotatedRect类型

【RotatedRect类型】
三个重要属性 center、size、angle
image
image
image

boundingRect()
//通过轮廓点,找到其外接矩形Rect(水平) 
fitEllipse()
//通过轮廓点,用最小二乘法拟合出一个外接椭圆,函数会返回椭圆的内接旋转矩形RotatedRect 
minEnclosingCircle( )
//通过轮廓点,找到最小面积的包含圆(注意不是外接圆)

eg

int main() {  
// 加载图像,转换为灰度图,应用阈值处理以获取二值图像  
cv::Mat src = cv::imread("path_to_image.jpg", cv::IMREAD_GRAYSCALE);  
cv::Mat binary;  
cv::threshold(src, binary, 127, 255, cv::THRESH_BINARY);  

// 查找轮廓  
std::vector<std::vector<cv::Point>> contours;  
cv::findContours(binary, contours, cv::RETR_EXTERNAL,cv::CHAIN_APPROX_SIMPLE);  
  
// 绘制边界矩形:在别的图像上绘制
cv::Mat drawing = cv::Mat::zeros(binary.size(), CV_8UC3);  
for (int i = 0; i < contours.size(); i++) {  
    cv::Rect rect = cv::boundingRect(contours[i]);  
    cv::rectangle(drawing, rect, cv::Scalar(0, 255, 0), 2, 8, 0);  
}  
  
// 显示结果  
cv::imshow("Bounding Rectangles", drawing);  
cv::waitKey(0);  
return 0;  
}

投影变换

一般用于把图像根据变换关系转化成正视图以便进行模板匹配、SVM匹配等操作
将二维图像转换为具有三维空间显示效果的图像,如全景拼接、图像透视矫正等
image

重映射:把一幅图像中某位置的像素放置到另一个图片指定位置的过程

//根据给定的映射(函数)改变图像中每个像素点的位置
remap(InputArray src, OutputArraydst, InputArray map1, InputArray map2,int interpolation)
//进行透视变换
void warpPerspective( InputArray src, OutputArray dst,
                      InputArray M, Size dsize,
                      int flags = INTER_LINEAR,
                      int borderMode = BORDER_CONSTANT,
                      const Scalar& borderValue = Scalar());

M:3x3的变换矩阵,Mat类型 需要使用getPerspectiveTransform()函数获取
dsize:输出图像的大小 Size(double,double)

//获得透视变换所需的矩阵(4个点)
getPerspectiveTransform(const Point2f src[], const Point2f dst[], int solveMethod = DECOMP_LU)

Point2f src[4] = {Point2f(160,300),Point2f(931,253),Point2f(105,787),Point2f(1000,859)};
Point2f dst[4] = {Point2f(0,0),Point2f(380,0),Point2f(0,360),Point2f(380,360)};

仿射变换

它保持了二维图形的平直性(直线经过变换之后依然是直线)和平行性(二维图形之间的相对位置关系保持不变,平行线依然是平行线,且直线上点的位置顺序不变)。
主要用于二维坐标到二维坐标之间的线性变换,如平移、旋转、缩放等
image

warpAffine(InputArray src, OutputArray dst, InputArray mat, Size dsize = Size(), int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, Scalar borderValue = Scalar())//进行仿射变换

mat:2x3的变换矩阵 从getAffineTransform()中获得
dsize:输出图像的大小,如果这个参数为 Size() ,则输出图像的大小将与输入图像相同。

//获得仿射变换所需的矩阵
getAffineTransform(InputArray src, OutputArray dst)

只需要三个点 线性变换+平移变换
image
只需确定6个参数

图像输入输出

imread( ) //根据路径读取一张图片
imwrite( )  //向对应路径写入一张图像
imreadmulti( ) //一次读取多张图片

Video视频模块

class VideoCapture():读取视频

构建一个视频捕获类,捕获的视频可以以每一帧图像的形式保存到Mat中
这个类能够通过get(),set()方法获取和设置一些相机参数

int main(){
	//从文件中读取视频
    cv::VideoCapture capture(const string& filename);
	//filename:视频位置
	
	//打开摄像头 索引0
	cv::VideoCapture cap(0);
	
	//检查是否成功打开摄像头
	if(!cap.isOpened()){
		std::cerr<<"Error..."<<std::endl;//cerr无缓冲输出,适合输出错误消息
		return -1;
	}
	
	//获取并打印帧率
	double fps=cap.get(cv::CAP_PROP_FPS);
	std::cout<<"fps:"<<fps<<std::endl;
	
	//设置帧率:不是所有摄像头都支持
	cap.set(cv::CAP_PROP_FPS,30.0);

    //创建窗口来显示视频帧
    cv::namedWindow("Video Capture",cv::WINDOW_AUTOSIZE);

    cv::Mat frame;
    while(true){
        //读取下一帧
        cap>>frame;

        //检查是否成功读取帧
        if(frame.empty()){
            std::cerr<<"Error..."<<std::endl;//cerr无缓冲输出,适合输出错误消息
            break;
        }

        //显示帧
        cv::imshow("Video Capture",frame);

        //按下ESC结束程序
        if(cv::waitKey(2)==27){
            break;
        }
    }
    //释放摄像头,关闭窗口
    cap.release();
    cv::destroyAllWindows();

    return 0;
}

在使用 VideoCapture 类时,确保在程序结束时释放摄像头资源(使用 release() 方法)
并关闭所有OpenCV窗口(使用 destroyAllWindows() 方法)

class VideoWriter():录制视频

构建一个视频保存类,能够方便地保存视频,并且提供各种格式
在实验室时无法模拟赛场的光线环境,常常在比赛时录制相机第一视角的视频,以供之后测试使用
也可以把检测完的每一帧图片连成视频,保存下来,之后根据这个视频来查找问题、改进算法

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;

//保存avi视频文件
void main(){
    VideoCapture cap;
    cap.open("d:\\input.avi");
    Size sizeReturn = Size(cap.get(CV_CAP_PROP_FRAME_WIDTH),cap.get(CV_CAP_PROP_FRAME_HEIGHT));
    VideoWriter writer("d:\\output.avi",cv::VideoWriter::fourcc('M', 'J', 'P', 'G'),cap.get(CV_CAP_PROP_FPS),sizeReturn,false);
 
    if(!cap.isOpened())
        return;
 
    Mat frame;
    while(true)
    {
        cap>>frame;
        if(frame.empty())
            break;
        writer<<frame;//等同于writer.write(frame);
        imshow("video", frame);
        if(waitKey(20)>0)
            break;
    }
    cout<<"write end!";
    cap.release();
    destroyAllWindows();
}

highgui模块

imshow() 指定窗口显示一张图片

waitKey(int delay=0)等待用户按键输入
key默认为0:程序会一直等待用户按键输入,直到用户手动关闭窗口为止

if (waitKey(0) == 27) { // 如果用户按下 ESC 键,退出循环
      break;
}
waitKey(500);//每500秒显示一次图像
posted @ 2024-11-15 17:47  White_ink  阅读(14)  评论(0编辑  收藏  举报