OpenCV图像处理入门:10个实用案例带你掌握核心功能

OpenCV图像处理入门:10个实用案例带你掌握核心功能

【免费下载链接】opencv OpenCV: 开源计算机视觉库 【免费下载链接】opencv 项目地址: https://gitcode.com/gh_mirrors/opencv31/opencv

OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库,它提供了丰富的图像处理和计算机视觉算法。本文将通过10个实用案例,带你快速入门OpenCV的核心功能,从基础的图像绘制到复杂的特征检测,让你能够轻松应对各种图像处理任务。

1. 图像绘制:创建自定义图形

在图像处理中,经常需要绘制各种几何图形来标记目标或添加注释。OpenCV提供了一系列绘图函数,可以轻松绘制直线、矩形、圆形、椭圆和多边形等。

以下是一个使用OpenCV绘制基本图形的示例代码,来自samples/cpp/tutorial_code/ImgProc/basic_drawing/Drawing_1.cpp

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

#define w 400

using namespace cv;

void MyEllipse( Mat img, double angle );
void MyFilledCircle( Mat img, Point center );
void MyPolygon( Mat img );
void MyLine( Mat img, Point start, Point end );

int main( void ){
  char atom_window[] = "Drawing 1: Atom";
  char rook_window[] = "Drawing 2: Rook";

  Mat atom_image = Mat::zeros( w, w, CV_8UC3 );
  Mat rook_image = Mat::zeros( w, w, CV_8UC3 );

  MyEllipse( atom_image, 90 );
  MyEllipse( atom_image, 0 );
  MyEllipse( atom_image, 45 );
  MyEllipse( atom_image, -45 );
  MyFilledCircle( atom_image, Point( w/2, w/2) );

  MyPolygon( rook_image );
  rectangle( rook_image, Point( 0, 7*w/8 ), Point( w, w), Scalar( 0, 255, 255 ), FILLED, LINE_8 );
  MyLine( rook_image, Point( 0, 15*w/16 ), Point( w, 15*w/16 ) );
  MyLine( rook_image, Point( w/4, 7*w/8 ), Point( w/4, w ) );
  MyLine( rook_image, Point( w/2, 7*w/8 ), Point( w/2, w ) );
  MyLine( rook_image, Point( 3*w/4, 7*w/8 ), Point( 3*w/4, w ) );

  imshow( atom_window, atom_image );
  moveWindow( atom_window, 0, 200 );
  imshow( rook_window, rook_image );
  moveWindow( rook_window, w, 200 );

  waitKey( 0 );
  return(0);
}

void MyEllipse( Mat img, double angle )
{
  int thickness = 2;
  int lineType = 8;

  ellipse( img, Point( w/2, w/2 ), Size( w/4, w/16 ), angle, 0, 360, Scalar( 255, 0, 0 ), thickness, lineType );
}

void MyFilledCircle( Mat img, Point center )
{
  circle( img, center, w/32, Scalar( 0, 0, 255 ), FILLED, LINE_8 );
}

void MyPolygon( Mat img )
{
  int lineType = LINE_8;

  Point rook_points[1][20];
  rook_points[0][0]  = Point(    w/4,   7*w/8 );
  rook_points[0][1]  = Point(  3*w/4,   7*w/8 );
  rook_points[0][2]  = Point(  3*w/4,  13*w/16 );
  rook_points[0][3]  = Point( 11*w/16, 13*w/16 );
  rook_points[0][4]  = Point( 19*w/32,  3*w/8 );
  rook_points[0][5]  = Point(  3*w/4,   3*w/8 );
  rook_points[0][6]  = Point(  3*w/4,     w/8 );
  rook_points[0][7]  = Point( 26*w/40,    w/8 );
  rook_points[0][8]  = Point( 26*w/40,    w/4 );
  rook_points[0][9]  = Point( 22*w/40,    w/4 );
  rook_points[0][10] = Point( 22*w/40,    w/8 );
  rook_points[0][11] = Point( 18*w/40,    w/8 );
  rook_points[0][12] = Point( 18*w/40,    w/4 );
  rook_points[0][13] = Point( 14*w/40,    w/4 );
  rook_points[0][14] = Point( 14*w/40,    w/8 );
  rook_points[0][15] = Point(    w/4,     w/8 );
  rook_points[0][16] = Point(    w/4,   3*w/8 );
  rook_points[0][17] = Point( 13*w/32,  3*w/8 );
  rook_points[0][18] = Point(  5*w/16, 13*w/16 );
  rook_points[0][19] = Point(    w/4,  13*w/16 );

  const Point* ppt[1] = { rook_points[0] };
  int npt[] = { 20 };

  fillPoly( img, ppt, npt, 1, Scalar( 255, 255, 255 ), lineType );
}

void MyLine( Mat img, Point start, Point end )
{
  int thickness = 2;
  int lineType = LINE_8;

  line( img, start, end, Scalar( 0, 0, 0 ), thickness, lineType );
}

通过这个示例,你可以学习如何使用OpenCV的ellipse()circle()fillPoly()line()等函数绘制各种图形。这些函数提供了丰富的参数,可以控制图形的位置、大小、颜色、线条粗细等属性。

2. 傅里叶变换:去除图像周期噪声

傅里叶变换是一种强大的数学工具,可以将图像从空间域转换到频率域,从而实现对图像频率成分的分析和处理。在图像处理中,傅里叶变换常用于去除周期噪声、图像增强等任务。

以下是一个使用傅里叶变换去除图像周期噪声的示例代码,来自samples/cpp/tutorial_code/ImgProc/periodic_noise_removing_filter/periodic_noise_removing_filter.cpp

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

using namespace cv;
using namespace std;

void fftshift(const Mat& inputImg, Mat& outputImg);
void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H);
void synthesizeFilterH(Mat& inputOutput_H, Point center, int radius);
void calcPSD(const Mat& inputImg, Mat& outputImg, int flag = 0);

int main(int argc, char* argv[])
{
    CommandLineParser parser(argc, argv, "{help h usage ? | | print this message   }{@image |period_input.jpg | input image name     }");
    string strInFileName = parser.get<String>("@image");
    samples::addSamplesDataSearchSubDirectory("doc/tutorials/imgproc/periodic_noise_removing_filter/images");

    Mat imgIn = imread(samples::findFile(strInFileName), IMREAD_GRAYSCALE);
    if (imgIn.empty())
    {
        cout << "ERROR : Image cannot be loaded..!!" << endl;
        return -1;
    }

    imshow("Image corrupted", imgIn);
    imgIn.convertTo(imgIn, CV_32F);

    Rect roi = Rect(0, 0, imgIn.cols & -2, imgIn.rows & -2);
    imgIn = imgIn(roi);

    Mat imgPSD;
    calcPSD(imgIn, imgPSD);
    fftshift(imgPSD, imgPSD);
    normalize(imgPSD, imgPSD, 0, 255, NORM_MINMAX);

    Mat H = Mat(roi.size(), CV_32F, Scalar(1));
    const int r = 21;
    synthesizeFilterH(H, Point(705, 458), r);
    synthesizeFilterH(H, Point(850, 391), r);
    synthesizeFilterH(H, Point(993, 325), r);

    Mat imgOut;
    fftshift(H, H);
    filter2DFreq(imgIn, imgOut, H);

    imgOut.convertTo(imgOut, CV_8U);
    normalize(imgOut, imgOut, 0, 255, NORM_MINMAX);
    imwrite("result.jpg", imgOut);
    imwrite("PSD.jpg", imgPSD);
    fftshift(H, H);
    normalize(H, H, 0, 255, NORM_MINMAX);
    imshow("Debluring", imgOut);
    waitKey(0);
    return 0;
}

void fftshift(const Mat& inputImg, Mat& outputImg)
{
    outputImg = inputImg.clone();
    int cx = outputImg.cols / 2;
    int cy = outputImg.rows / 2;
    Mat q0(outputImg, Rect(0, 0, cx, cy));
    Mat q1(outputImg, Rect(cx, 0, cx, cy));
    Mat q2(outputImg, Rect(0, cy, cx, cy));
    Mat q3(outputImg, Rect(cx, cy, cx, cy));
    Mat tmp;
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);
    q1.copyTo(tmp);
    q2.copyTo(q1);
    tmp.copyTo(q2);
}

void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H)
{
    Mat planes[2] = { Mat_<float>(inputImg.clone()), Mat::zeros(inputImg.size(), CV_32F) };
    Mat complexI;
    merge(planes, 2, complexI);
    dft(complexI, complexI, DFT_SCALE);

    Mat planesH[2] = { Mat_<float>(H.clone()), Mat::zeros(H.size(), CV_32F) };
    Mat complexH;
    merge(planesH, 2, complexH);
    Mat complexIH;
    mulSpectrums(complexI, complexH, complexIH, 0);

    idft(complexIH, complexIH);
    split(complexIH, planes);
    outputImg = planes[0];
}

void synthesizeFilterH(Mat& inputOutput_H, Point center, int radius)
{
    Point c2 = center, c3 = center, c4 = center;
    c2.y = inputOutput_H.rows - center.y;
    c3.x = inputOutput_H.cols - center.x;
    c4 = Point(c3.x,c2.y);
    circle(inputOutput_H, center, radius, 0, -1, 8);
    circle(inputOutput_H, c2, radius, 0, -1, 8);
    circle(inputOutput_H, c3, radius, 0, -1, 8);
    circle(inputOutput_H, c4, radius, 0, -1, 8);
}

void calcPSD(const Mat& inputImg, Mat& outputImg, int flag)
{
    Mat planes[2] = { Mat_<float>(inputImg.clone()), Mat::zeros(inputImg.size(), CV_32F) };
    Mat complexI;
    merge(planes, 2, complexI);
    dft(complexI, complexI);
    split(complexI, planes);

    planes[0].at<float>(0) = 0;
    planes[1].at<float>(0) = 0;

    Mat imgPSD;
    magnitude(planes[0], planes[1], imgPSD);
    pow(imgPSD, 2, imgPSD);
    outputImg = imgPSD;

    if (flag)
    {
        Mat imglogPSD;
        imglogPSD = imgPSD + Scalar::all(1);
        log(imglogPSD, imglogPSD);
        outputImg = imglogPSD;
    }
}

在这个示例中,首先计算输入图像的功率谱密度(PSD),然后在频率域中设计一个滤波器来去除周期噪声对应的频率成分,最后通过傅里叶逆变换将处理后的频率域图像转换回空间域,得到去噪后的图像。

3. 形态学操作:击中-击不中变换

形态学操作是基于图像形状的一种图像处理方法,常用的形态学操作包括腐蚀、膨胀、开运算、闭运算等。击中-击不中变换是一种特殊的形态学操作,常用于目标检测和模式识别。

以下是一个使用击中-击不中变换的示例代码,来自samples/cpp/tutorial_code/ImgProc/HitMiss/HitMiss.cpp

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

using namespace cv;

int main(){
    Mat input_image = (Mat_<uchar>(8, 8) <<
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 255, 255, 255, 0, 0, 0, 255,
        0, 255, 255, 255, 0, 0, 0, 0,
        0, 255, 255, 255, 0, 255, 0, 0,
        0, 0, 255, 0, 0, 0, 0, 0,
        0, 0, 255, 0, 0, 255, 255, 0,
        0, 255, 0, 255, 0, 0, 255, 0,
        0, 255, 255, 255, 0, 0, 0, 0);

    Mat kernel = (Mat_<int>(3, 3) <<
        0, 1, 0,
        1, -1, 1,
        0, 1, 0);

    Mat output_image;
    morphologyEx(input_image, output_image, MORPH_HITMISS, kernel);

    const int rate = 50;
    kernel = (kernel + 1) * 127;
    kernel.convertTo(kernel, CV_8U);

    resize(kernel, kernel, Size(), rate, rate, INTER_NEAREST);
    imshow("kernel", kernel);
    moveWindow("kernel", 0, 0);

    resize(input_image, input_image, Size(), rate, rate, INTER_NEAREST);
    imshow("Original", input_image);
    moveWindow("Original", 0, 200);

    resize(output_image, output_image, Size(), rate, rate, INTER_NEAREST);
    imshow("Hit or Miss", output_image);
    moveWindow("Hit or Miss", 500, 200);

    waitKey(0);
    return 0;
}

在这个示例中,定义了一个8x8的输入图像和一个3x3的结构元素( kernel )。通过morphologyEx()函数应用击中-击不中变换,检测输入图像中与结构元素匹配的模式。

4. 图像滤波:高斯模糊

高斯模糊是一种常用的图像平滑技术,通过卷积操作将图像中的高频成分(如噪声、边缘)减弱,从而实现图像模糊效果。高斯模糊的核心是使用高斯核进行卷积运算。

OpenCV提供了GaussianBlur()函数来实现高斯模糊,其函数原型如下:

void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT);

其中,src是输入图像,dst是输出图像,ksize是高斯核的大小,sigmaXsigmaY分别是X方向和Y方向的高斯标准差。

以下是一个使用高斯模糊的简单示例:

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

int main() {
    Mat src = imread("samples/data/lena.jpg");
    if (src.empty()) {
        return -1;
    }

    Mat dst;
    GaussianBlur(src, dst, Size(5, 5), 0, 0);

    imshow("Original", src);
    imshow("Gaussian Blur", dst);
    waitKey(0);
    return 0;
}

在这个示例中,使用5x5的高斯核对图像进行模糊处理。你可以通过调整高斯核的大小和标准差来控制模糊程度。

Lena原图

5. 边缘检测:Canny算子

边缘检测是图像处理中的一项基本任务,用于检测图像中物体的边缘。Canny算子是一种常用的边缘检测算法,具有良好的边缘定位能力和抗噪声能力。

OpenCV提供了Canny()函数来实现Canny边缘检测,其函数原型如下:

void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false);

其中,image是输入图像(灰度图),edges是输出的边缘图像,threshold1threshold2是高低阈值,apertureSize是Sobel算子的孔径大小,L2gradient表示是否使用L2范数计算梯度幅值。

以下是一个使用Canny算子进行边缘检测的示例:

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

int main() {
    Mat src = imread("samples/data/lena.jpg", IMREAD_GRAYSCALE);
    if (src.empty()) {
        return -1;
    }

    Mat edges;
    Canny(src, edges, 50, 150);

    imshow("Original", src);
    imshow("Canny Edges", edges);
    waitKey(0);
    return 0;
}

在这个示例中,首先将彩色图像转换为灰度图,然后使用Canny算子检测边缘,高低阈值分别设置为50和150。

6. 轮廓检测:寻找物体轮廓

轮廓是图像中具有相同颜色或灰度的连续曲线,代表了物体的边界。轮廓检测在目标识别、图像分割等领域有着广泛的应用。

OpenCV提供了findContours()函数来寻找图像中的轮廓,其函数原型如下:

void findContours(InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset = Point());

其中,image是输入图像(二值图),contours是输出的轮廓列表,hierarchy是轮廓的层次结构信息,mode是轮廓检索模式,method是轮廓近似方法。

以下是一个使用轮廓检测的示例:

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

int main() {
    Mat src = imread("samples/data/shapes.jpg");
    if (src.empty()) {
        return -1;
    }

    Mat gray, binary;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    threshold(gray, binary, 200, 255, THRESH_BINARY_INV);

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

    Mat dst = src.clone();
    drawContours(dst, contours, -1, Scalar(0, 0, 255), 2);

    imshow("Original", src);
    imshow("Contours", dst);
    waitKey(0);
    return 0;
}

在这个示例中,首先将彩色图像转换为灰度图,然后进行二值化处理,接着使用findContours()函数寻找轮廓,最后使用drawContours()函数将轮廓绘制在原图上。

7. 图像变换:透视变换

透视变换是一种将图像从一个透视角度转换到另一个透视角度的技术,常用于校正图像的透视畸变,如将倾斜拍摄的文档校正为正视图。

OpenCV提供了getPerspectiveTransform()函数来计算透视变换矩阵,以及warpPerspective()函数来应用透视变换,其函数原型如下:

Mat getPerspectiveTransform(const Point2f src[], const Point2f dst[], int solveMethod = DECOMP_LU);
void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, const Scalar& borderValue = Scalar());

其中,srcdst分别是源图像和目标图像的四个对应点,M是透视变换矩阵,dsize是输出图像的大小。

以下是一个使用透视变换校正文档图像的示例:

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

int main() {
    Mat src = imread("samples/data/document.jpg");
    if (src.empty()) {
        return -1;
    }

    vector<Point2f> src_pts = {Point2f(50, 50), Point2f(200, 50), Point2f(50, 200), Point2f(200, 200)};
    vector<Point2f> dst_pts = {Point2f(0, 0), Point2f(250, 0), Point2f(0, 250), Point2f(250, 250)};

    Mat M = getPerspectiveTransform(src_pts, dst_pts);
    Mat dst;
    warpPerspective(src, dst, M, Size(250, 250));

    imshow("Original", src);
    imshow("Perspective Transform", dst);
    waitKey(0);
    return 0;
}

在这个示例中,定义了源图像和目标图像的四个对应点,计算透视变换矩阵后应用透视变换,将倾斜的文档校正为正视图。

8. 特征检测:SIFT算法

SIFT(Scale-Invariant Feature Transform)是一种具有尺度不变性的特征检测算法,能够在不同尺度、旋转、光照条件下检测到稳定的特征点。

OpenCV提供了SIFT算法的实现,以下是一个使用SIFT检测图像特征点的示例:

#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
using namespace cv;
using namespace cv::xfeatures2d;

int main() {
    Mat src = imread("samples/data/lena.jpg");
    if (src.empty()) {
        return -1;
    }

    Ptr<SIFT> sift = SIFT::create();
    vector<KeyPoint> keypoints;
    Mat descriptors;
    sift->detectAndCompute(src, noArray(), keypoints, descriptors);

    Mat dst;
    drawKeypoints(src, keypoints, dst, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

    imshow("SIFT Keypoints", dst);
    waitKey(0);
    return 0;
}

在这个示例中,使用SIFT算法检测图像的特征点,并将特征点绘制在原图上。每个特征点用一个圆圈表示,圆圈的大小表示特征点的尺度,方向表示特征点的主方向。

9. 目标检测:Haar级联分类器

Haar级联分类器是一种基于机器学习的目标检测方法,通过训练大量的正样本和负样本,构建级联分类器来检测目标。OpenCV提供了预训练的Haar级联分类器,可用于检测人脸、眼睛等目标。

以下是一个使用Haar级联分类器检测人脸的示例:

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

int main() {
    CascadeClassifier face_cascade;
    if (!face_cascade.load("data/haarcascades/haarcascade_frontalface_default.xml")) {
        return -1;
    }

    Mat src = imread("samples/data/face.jpg");
    if (src.empty()) {
        return -1;
    }

    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    equalizeHist(gray, gray);

    vector<Rect> faces;
    face_cascade.detectMultiScale(gray, faces, 1.1, 2, 0 | CASCADE_SCALE_IMAGE, Size(30, 30));

    for (size_t i = 0; i < faces.size(); i++) {
        rectangle(src, faces[i], Scalar(0, 255, 0), 2);
    }

    imshow("Face Detection", src);
    waitKey(0);
    return 0;
}

在这个示例中,加载预训练的人脸检测Haar级联分类器,然后对输入图像进行灰度化和直方图均衡化处理,接着使用detectMultiScale()函数检测人脸,最后将检测到的人脸用矩形框标记出来。

10. 视频处理:读取和播放视频

OpenCV不仅可以处理图像,还可以处理视频。通过VideoCapture类可以读取视频文件或摄像头输入,通过imshow()函数可以播放视频。

以下是一个读取和播放视频文件的示例:

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

int main() {
    VideoCapture cap("samples/data/vtest.avi");
    if (!cap.isOpened()) {
        return -1;
    }

    Mat frame;
    while (cap.read(frame)) {
        imshow("Video", frame);
        if (waitKey(30) == 27) {
            break;
        }
    }

    cap.release();
    destroyAllWindows();
    return 0;
}

在这个示例中,使用VideoCapture类打开视频文件,然后循环读取视频帧并显示,按下ESC键退出视频播放。

总结

本文介绍了OpenCV的10个实用案例,涵盖了图像绘制、傅里叶变换、形态学操作、图像滤波、边缘检测、轮廓检测、图像变换、特征检测、目标检测和视频处理等核心功能。通过这些案例,你可以快速入门OpenCV图像处理,并将其应用到实际项目中。

OpenCV的功能远不止于此,还有更多高级的算法和技术等待你去探索。建议你参考OpenCV的官方文档和示例代码,深入学习OpenCV的更多功能。

官方文档:doc/root.markdown.in 示例代码:samples/cpp

希望本文对你有所帮助,祝你在OpenCV的学习之路上取得进步!

【免费下载链接】opencv OpenCV: 开源计算机视觉库 【免费下载链接】opencv 项目地址: https://gitcode.com/gh_mirrors/opencv31/opencv

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值