人工智能 物联网 大数据 云计算

2018-10-25【机器视觉笔录】OpenCV中的API及用法

2018-11-25  本文已影响0人  狐二丶
--------------------------------
Author : ShawnDong
updateDate :2018.11.25
Blog : ShawnDong98.github.io
--------------------------------

C++部分

图像的加载显示与保存

加载图像(cv::imread)

显示图像(cv::nameWindows与cv::imshow)

保存图像(cv::imwrite)

矩阵的掩膜操作

获取图像指针

像素范围处理

掩膜操作实现图像对比度调整

ps: 感觉和卷积很相似



代码实现:

    int cols = (src.cols - 1) * src.channels();
    int offsetx = src.channels();
    int rows = src.rows;
    dst = Mat::zeros(src.size(), src.type());
    for (int row = 1; row < (rows - 1); row++)
    {
        const uchar* previous = src.ptr<uchar>(row - 1);
        const uchar* current = src.ptr<uchar>(row);
        const uchar* next = src.ptr<uchar>(row + 1);
        uchar* output = dst.ptr<uchar>(row);
        for (int col = offsetx; col < cols; col++)
        {
            output[col] = saturate_cast<uchar>(5 * current[col] - (current[col - offsetx] 
                                               + current[col + offsetx] + previous[col] + next[col]));
        }
    }

函数调用filer2D功能

  1. 定义掩膜:Mat kernel = (Mat_<char>(3, 3)<<0, -1, 0, -1, 5, -1, 0, -1, 0);
  2. filter2D(src, dst, src.depth(), kernel);其中src与dst是Mat类型变量,src.depth表示位图深度, 有32, 24, 8等。
    代码实现:可以实现和上面代码同样的功能
    double t;
    t = (double)getTickCount();
    filter2D(src, dst, src.depth(), kernel);
    t = ((double)getTickCount() - t) / getTickFrequency();
    cout << "Built in Filter2D time passed in seconds" << t << endl;

Mat对象

Mat对象与IplImage对象

Mat对象构造函数与常用方法
构造函数:

常用方法:

Mat对象使用

Mat A = imread(imgFilePath);
Mat B(A) //只复制
Mat F = A.clone();
Mat G;
A.copyTo(G)

Mat对象使用的四个要点

Mat对象创建

Mat M;
M.create(4, 3, CV_8UC2);
M = Scalar(127, 127);

定义小数组

Mat C = (Mat_<double>(3, 3) <<  0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << C << endl << endl;

图像操作

   cvtColor(src, gray_src, CV_BGR2GRAY);
   int height = gray_src.rows;
   int width = gray_src.cols;
   for(int row = 0; row < height; row++)
   {
       for (int col = 0; col < width; col++)
       {
           int gray = gray_src.at<uchar>(row, col);
           gray_src.at<uchar>(row, col) = 255 - gray;
       }
   }
for (int row = 0; row < height; row++)
    {
        for (int col = 0; col < width; col++)
        {
            if (nc == 1)
            {
                int gray = gray_src.at<uchar>(row, col);
                gray_src.at<uchar>(row, col) = 255 - gray;
            }
            else if (nc == 3)
            {
                int b = src.at<Vec3b>(row, col)[0];
                int g = src.at<Vec3b>(row, col)[1];
                int r = src.at<Vec3b>(row, col)[2];
                dst.at<Vec3b>(row, col)[0] = 255 - b;
                dst.at<Vec3b>(row, col)[1] = 255 - g;
                dst.at<Vec3b>(row, col)[2] = 255 - r;
            }
        }
    }

其实以上代码和 bitwise_not(src. dst) 这个api效果是一样的

Vec3b与Vec3F

图像混合

void cv::addWeighted(inputArray src1,
                     double alpha,
                     inputArray src2,
                     double beta,
                     double gamma,
                     OutputArray dst,
                     int dtype = -1
                    )

参数1: 输入图像Mat - src1
参数2: 输入图像src1的alpha值
参数3: 输入图像Mat - src2
参数4: 输入图像src2的alpha值
参数5: gamma值
参数6: 输出混合图像
注意:两张图像的大小和类型必须一致才可以

    double alpha = 0.5;
    if (src1.rows == src2.rows && src1.cols == src2.cols && src1.type() == src2.type())
    {
        addWeighted(src1, alpha, src2, (1.0 - alpha), 0.0, dst);
        //multiply(src1, src2, dst, 1.0);
        namedWindow("blend demo ", CV_WINDOW_AUTOSIZE);
        imshow("blend demo", dst);

    }

调整图像亮度和对比度

理论
图像变换可以看作如下:

代码演示

for (int row = 0; row < height; row++)
    {
        for (int col = 0; col < width; col++)
        {
            if (nc == 1)
            {
                int v = src.at<uchar>(row, col);
                dst.at<uchar>(row, col) = 255 - v;
            }
            else if (nc == 3)
            {
                int b = src.at<Vec3b>(row, col)[0];
                int g = src.at<Vec3b>(row, col)[1];
                int r = src.at<Vec3b>(row, col)[2];
                dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b*alpha + beta);
                dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g*alpha + beta);
                dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r*alpha + beta);
            }
        }

其中alpha控制对比度,beta控制亮度。

重要的API

绘制形状和文字

模糊操作一


假设有6x6的图像像素点矩阵
卷积过程:6x6上面是个3x3的窗口,从左到右,从上向下移动,黄色的每个像素点值之和取平均值赋给中心红色像素作为它卷积处理之后新的像素值。每次移动一个像素格。(均值滤波)

模糊操作二

相关API

膨胀与腐蚀

形态学操作(morphology operators)

形态学操作-膨胀

形态学操作-腐蚀

相关API

代码演示:

void CallBack_Demo(int, void*)
{
    int s = element_size * 2 + 1;
    Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
    //dilate(src, dst, structureElement, Point(-1, -1), 1);
    erode(src, dst, structureElement, Point(-1, -1));
    imshow(OUTPUT_WIN, dst);
}

动态调整结构元素大小

代码演示:

createTrackbar("Element Size:", OUTPUT_WIN, &element_size, max_size, CallBack_Demo);

形态学操作

总结:闭操作用于去掉小黑点,开操作用于去掉小白点

相关API

代码示例:

    namedWindow(OUTPUT_WIN, CV_WINDOW_AUTOSIZE);
    Mat kernel = getStructuringElement(MORPH_RECT, Size(11, 11), Point(-1, -1));
    morphologyEx(src, dst,CV_MOP_CLOSE, kernel, Point(-1, -1));
    imshow(OUTPUT_WIN, dst);

形态学操作应用-提取水平和垂直直线

原理方法

图像形态学操作时候,可以通过自定义的结构元素实现结构元素对输入图像一些对象敏感、另外一些对象不敏感,这样就会让敏感的对象改变而不敏感的对象保留输出。通过使用两个最基本的形态学操作-膨胀与腐蚀,使用不同的结构元素实现对输入图像的操作,得到想要的结果。

结构元素

提取步骤

转换为二值图像-adaptiveThreshold

adativeThreshold(
Mat src, //输入的灰度图像
Mat dst, //二值图像
double maxValue, //二值图像最大值
int adaptiveMethod, //自适应方法,只能其中之一
                   //ADAPTIVE_THRESH_MEAN_C, ADAPTIVE_THRESH_GAUSSIAN_C
int thresholdType, //阈值类型
int blocksize, //块大小
double C //常量C 可以是正数, 0, 负数
)

注意:自适应阈值二值化的方式通过计算每个像素周围bxb大小像素块的加权均值并减去常量C得到,如果图片像素比较高,像素块又比较小,有可能会出现二值不好的情况,比如这样,黑色的部分被二值成了白色。


代码实现:

void Binarry_Line()
{
    Mat gray_src;
    cvtColor(src, gray_src, CV_BGR2GRAY);
    namedWindow("gray_image", CV_WINDOW_NORMAL);
    cvResizeWindow("gray_image", 500, 500);
    imshow("gray_image", gray_src);

    Mat binImg;
    threshold(gray_src, binImg, 80, 255, THRESH_BINARY_INV);
    //adaptiveThreshold(gray_src, binImg, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 101, -2);
    namedWindow("binarry image", CV_WINDOW_NORMAL);
    cvResizeWindow("binarry image", 500, 500);
    imshow("binarry image", binImg);

    Mat hline = getStructuringElement(MORPH_RECT, Size(src.cols / 16, 1), Point(-1, -1));
    Mat vline = getStructuringElement(MORPH_RECT, Size(1, src.rows / 16), Point(-1, -1));

    Mat temp;
    erode(binImg, temp, hline);
    dilate(temp, dst, hline);
    namedWindow("final image", CV_WINDOW_NORMAL);
    cvResizeWindow("final image", 500, 500);
    imshow("final image", dst);
    //createTrackbar("Element Size:", OUTPUT_WIN, &element_size, max_size, CallBack_Demo);
    //CallBack_Demo(0, 0);
}

图像上采样和降采样

  1. 我们在图像处理中常常会调整图像大小,最常见的就是放大(zoom in)和缩小(zoom out)。
  2. 一个图像金子塔是一系列的图像组成,最底下一张是图像尺寸最大,最上方的图像尺寸最小,从空间上向下看就像一个古代的金字塔。
void Sample()
{
    //上采样
    Mat s_up;
    pyrUp(src, s_up, Size(src.cols * 2, src.rows * 2));
    namedWindow("sample up ", CV_WINDOW_AUTOSIZE);
    imshow("sample up ", s_up);

    //降采样
    Mat s_down;
    pyrDown(src, s_down, Size(src.cols / 2, src.rows / 2));
    namedWindow("sample down ", CV_WINDOW_AUTOSIZE);
    imshow("sample down ", s_down);

    Mat gray_src, g1, g2, dogImg;
    cvtColor(src, gray_src, CV_BGR2GRAY);
    GaussianBlur(gray_src, g1, Size(3, 3), 0, 0);
    GaussianBlur(g1, g2, Size(3, 3), 0, 0);
    subtract(g1, g2, dogImg, Mat());
    normalize(dogImg, dogImg, 255, 0, NORM_MINMAX);
    imshow("DOG Image", dogImg);
}

基本阈值操作(二值化)

代码演示

void Threshold_Demo(int, void*)
{

    cvtColor(src, gray_src, CV_BGR2GRAY);
    threshold(gray_src, dst, threshold_value, threshold_max, type_value);
    imshow(output_title, dst);
}

自定义线性滤波

卷积概念

卷积如何工作

卷积核又被称为算子
常见算子

void Robert()
{
    //Robert_X方向
    Mat kernel_x = (Mat_<int>(2, 2) << 1, 0, 0, -1);
    filter2D(src, dst, -1, kernel_x, Point(-1, -1), 0.0);
    namedWindow("Robert_X", WINDOW_AUTOSIZE);
    imshow("Robert_X", dst);

    //Robert_Y方向
    Mat ying;
    Mat kernel_y = (Mat_<int>(2, 2) << 0, 1, -1, 0);
    filter2D(src, ying, -1, kernel_y, Point(-1, -1), 0.0);
    namedWindow(output_title, WINDOW_AUTOSIZE);
    imshow(output_title, ying);
}

void Sobel()
{
    //Sobel_X方向的差异
    //Mat kernel_x = (Mat_<int>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);
    //Sobel_Y方向的差异
    Mat kernel_y= (Mat_<int>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
    filter2D(src, dst, -1, kernel_y, Point(-1, -1), 0.0);
    namedWindow("Sobel", WINDOW_AUTOSIZE);
    imshow("Sobel", dst);
}
void Laplasian()
{
    Mat kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);
    filter2D(src, dst, -1, kernel, Point(-1, -1), 0.0);
    namedWindow("Laplasian", WINDOW_AUTOSIZE);
    imshow("Laplasian", dst);
}

自定义卷积模糊
API:

filter2D(Mat src, //输入图像
         Mat  dst, //输出图像
         Mat  depth, //图像深度 32/8
         Mat  kernel, //卷积核/模板
         Point anchor, //锚点位置
         double delta //计算出来的像素+delta
         )

代码演示:

void Customer_Filter()
{
    while (true)
    {
        char c = waitKey(500);
        if (c == 27)
        {
            break;
        }
        int ksize = 4 + (index % 5) * 2 + 1;
        Mat kernel = Mat::ones(Size(ksize, ksize), CV_32F) / (float)(ksize * ksize);
        filter2D(src, dst, -1, kernel, Point(-1, -1));
        index++;
        namedWindow("customer_filter", WINDOW_AUTOSIZE);
        imshow("customer_filter", dst);

    }
}

处理卷积边缘

卷积边界问题

处理边缘

API说明

copyMakeBorder(
    Mat src, //输入图像
    Mat dst, //添加边缘图像
    int top,  //边缘长度, 一般上下左右都取相同值
    int bottom,
    int left,
    int right,
    int boderType,
    Scalar value
    )

Sobel算子

卷积应用-图像边缘提取

Sobel算子

API说明cv::Sobel

cv::Sobel(
    InputArray Src, //输入图像
    OutputArray dst, //输出图像,大小与输入图像一致
    int depth, //输出图像深度
    int dx, //x方向, 几阶导数
    int dy, //y方向, 几阶导数
    int ksize, //Sobel算子kernel大小, 必须是1、 3、5、 7、(单数)
    double scale = 1,
    double delta = 0,
    int borderType = BORDER_DEFAULT
    )

Laplacian算子

理论

在二阶微分的时候, 最大变化处的值为零即边缘是零值。通过二阶导数计算,依据此理论我们可以计算图像二阶导数,提取边缘。

image.png

Laplacian算子

处理流程

Canny边缘检测

图像的边缘检测的原理是检测出图像中所有灰度值变化较大的点,而且这些点连接起来就构成了若干线条,这些线条就可以称为图像的边缘

Canny算法介绍

Canny算法过程-五步

  1. 高斯模糊-GaussianBlur
  2. 灰度转换-cvtColor
  3. 计算梯度-Sobel/Scharr
  4. 非最大信号抑制
  5. 高低阈值输出二值图像

Canny算法介绍-非最大信号抑制


这里不是很明白

Canny算法介绍-高低阈值输出二值图像

相关API-cv::Canny

  Canny(
    inputArray src, //8-bit的输入图像
    OutputArray edges, //输出边缘图像, 一般都是二值图像
    double threshold1, //低阈值, 常取高阈值的1/2, 或者1/3
    double threshold2, //高阈值
    int aptertureSize, //Sobel算子的size, 通常3x3, 取值3
    boot L2gradient //选择true表示是L2来归一化, 否则用L1归一化
    )

寻找轮廓

相关API

void cv::findContours   (   InputOutputArray    image,
                            OutputArrayOfArrays     contours,
                            OutputArray     hierarchy,
                            int     mode,
                            int     method,
                            Point   offset = Point() 
                        )   

参数介绍:
image:输入图像,图像必须为8-bit单通道图像,图像中的非零像素将被视为1,0像素保留其像素值,故加载图像后会自动转换为二值图像。我们同样可以使用cv::compare,cv::inRange,cv::threshold,cv::adaptiveThreshold,cv::Canny等函数来创建二值图像,,如果第四个参数为cv::RETR_CCOMP或cv::RETR_FLOODFILL,输入图像可以是32-bit整型图像(CV_32SC1)

contours:检测到的轮廓,每个轮廓都是以点向量的形式进行存储即使用point类型的vector表示

hierarchy:可选的输出向量(std::vector),包含了图像的拓扑信息,作为轮廓数量的表示hierarchy包含了很多元素,每个轮廓contours[i]对应hierarchy中hierarchy[i][0]~hierarchy[i][3],分别表示后一个轮廓,前一个轮廓,父轮廓,内嵌轮廓的索引,如果没有对应项,则相应的hierarchy[i]设置为负数。

mode:轮廓检索模式,可以通过cv::RetrievalModes()查看详细信息,如下
RETR_EXTERNAL:表示只检测最外层轮廓,对所有轮廓设置hierarchy[i][2]=hierarchy[i][3]=-1
RETR_LIST:提取所有轮廓,并放置在list中,检测的轮廓不建立等级关系
RETR_CCOMP:提取所有轮廓,并将轮廓组织成双层结构(two-level hierarchy),顶层为连通域的外围边界,次层位内层边界
RETR_TREE:提取所有轮廓并重新建立网状轮廓结构
RETR_FLOODFILL:官网没有介绍,应该是洪水填充法

method:轮廓近似方法可以通过cv::ContourApproximationModes()查看详细信息
CHAIN_APPROX_NONE:获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1
CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素值,保留该方向的中点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息
CHAIN_APPROX_TC89_L1和CHAIN_APPROX_TC89_KCOS使用Teh-Chinl链逼近算法中的一种

Rect boundingRect(InputArray points)

参数介绍:
points:输入信息,可以为包含点的容器(vector)或是Mat。
返回包覆输入信息的最小正矩形。

RotatedRect minAreaRect(InputArray points)

参数介绍:
points:输入信息,可以为包含点的容器(vector)或是Mat。
返回包覆输入信息的最小斜矩形。

boundingRect和minAreaRect的区别如下图


void minEnclosingCircle(InputArray points, Point2f& center, float& radius)

参数介绍:
points:输入信息,可以为包含点的容器(vector)或是Mat。
center:包覆圆形的圆心。
radius:包覆圆形的半径。

霍夫变换-直线


公式由下往上倒推

霍夫直线变换介绍

ps:不是很明白

相关API学习

  cv::HoughLines(
      InputArray src, //输出图像,必须8bit的灰度图像
      OutputArray lines, //输出的极坐标来表示直线
      double rho,   //生成极坐标时候的像素扫描步长
      double theta, //生成极坐标时候的角度步长, 一般取值CV_PI/180
      int threshold, //阈值, 只有获得足够交点的极坐标点才被看作直线
      double srn=0, //是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换(就是进行金字塔的操作)
      double stn=0, //是否应用多尺度的霍夫变换,不过不是设置0表示经典霍夫变换
      double min_theta=0, //表示角度扫描范围0~180之间, 默认即可
      double max_theta=CV_PI
      )
  cv::HoughLineP(
      InputArray src,  //输入图像,必须8-bit的灰度图像 
      OutputArray lines,  //输出的坐标来表示直线
      double rho,  //生成极坐标时候的像素扫描步长
      double theta,  //生成极坐标的时候的角度步长,一般取值CV_PI/180
      int threshold,  //阈值,只有获得足够交点的极坐标才被看作是直线
      double minLineLength=0, //最小直线长度
      double maxLineGap=0 //最大间隔

霍夫圆检测

霍夫圆检测原理

相关API cv::HoughCircles

  HoughCircles(
    InputArray image, //输入图像,必须是8位的单通道灰度图像
    OutputArray circles, //输出结果,发现的圆信息
    int method, //方法 - HOUGH_GRADIENT
    double mindist, //10 最短距离 - 可以分辨是两个圆的,否则认为是同心圆
    double param1, //canny edge detection high threshold
    double param2, //中心点累加器阈值 - 候选圆心
    int minradius, //最小半径
    int maxradius //最大半径
    )

像素重映射

什么是像素重映射

简单点说就是把输入图像中各个像素按照一定的规则映射到另外一张图像的对应位置上去,形成一张新的图像

API介绍cv::remap

    Remap(
        InputArray src, //输入图像
        OutputArray dst, //输出图像
        InputArray map1, //x映射表 CV_32FC1 / CV_32FC2
        InputArray map2, //y映射表
        int interpolation, //选择的插值方法, 常见线性插值, 可选择立方等
        int boderMode, //BODER_CONSTANT
        const Scalar boderValue //color
        )

python部分

import cv2 as cv

色彩空间转换

cv.cvtColor()
参数:

imgae: 图片

    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    cv.imshow("gray", gray)
    hsv = cv.cvtColor(image, cv.COLOR_BGR2HSV)
    cv.imshow("hsv", hsv)
    yuv = cv.cvtColor(image, cv.COLOR_BGR2YUV)
    cv.imshow("yuv", yuv)

cv.inRange()

通道的分离与合并

cv.split(): 通道的分离
cv.merge(): 通道的合并

像素运算

: cv.add(m1, m2)
: cv.subtract(m1, m2)
: cv.multiply(m1, m2)
: cv.divide(m1,m2)
均值&方差: cv.meanStdDev(m1)

: cv.bitwise_and(m1,m2)
: cv.bitwise_or(m1, m2)
: cv.bitwise_not(m1)

cv.addWeighted: 调整对比度与两度

ROI(Range Of Interest)

泛洪填充

def fill_color_demo(image):
    copyImg = image.copy()
    h, w = image.shape[:2]
    mask = np.zeros([h+2, w+2], np.uint8)
    cv.floodFill(copyImg, mask, (30, 30), (0, 0, 255), (100, 100, 100), (50, 50, 50), cv.FLOODFILL_FIXED_RANGE)
    cv.imshow("fill_color_demo", copyImg)

各个参数的作用
从seedPoint这个点取出的像素值BGR, 然后这个BGR减去loDiff是最小的阈值,加上upDiff是最大的阈值。然后所有在这个阈值范围内的像素填充newVal

模糊操作

def custom_blur_demo(image):
    kernel = np.ones([5, 5], np.float32)/25
    dst = cv.filter2D(image, -1, kernel=kernel)
    cv.imshow("custom_blur_demo", dst)

边缘保留滤波(EPF)

def bi_demo(image):
    dst = cv.bilateralFilter(image, 0, 100, 15)
    cv.imshow("bi_demo", dst)
def shift_demo(image):
    dst = cv.pyrMeanShiftFiltering(image, 10, 50)
    cv.imshow("shift_demo", dst)

直方图的绘制

def plot_demo(image):
    plt.hist(image.ravel(), 256, [0, 256])
    plt.show()
def image_hist(image):
    color = ('blue', 'green', 'red')
    for i, color in enumerate(color):
        hist = cv.calcHist([image], [i], None, [256], [0, 256])
        plt.plot(hist, color=color)
        plt.xlim([0, 256])
    plt.show()
def equalHist_demo(image):
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    dst = cv.equalizeHist(gray)
    cv.imshow("equalHist_demo", dst)

局部直方图均衡化

def clahe_demo(image):
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
    dst = clahe.apply(gray)
    cv.imshow("clahe_demo", dst) 

直方图均衡化公式

巴氏距离
相关性
卡方

直方图的降维计算不明白是怎么算出来的
(这里不明白公式的推导)

def back_projection_demo():
    sample = cv.imread('sample.png')
    target = cv.imread('universe2.png')
    roi_hsv = cv.cvtColor(sample, cv.COLOR_BGR2HSV)
    target_hsv = cv.cvtColor(target, cv.COLOR_BGR2HSV)

    cv.imshow("roi_hsv", roi_hsv)
    cv.imshow("target_hsv", target_hsv)

    roiHist = cv.calcHist([roi_hsv], [0, 1], None, [32, 32], [0, 100, 0, 256])
    cv.normalize(roiHist, roiHist, 0, 255, cv.NORM_MINMAX)
    dst = cv.calcBackProject([target_hsv], [0, 1], roiHist, [0, 180, 0, 256], 1)
    cv.imshow("bacProjectionDemo", dst)

备注:以后查阅各API的参数及使用方法

模板匹配

简单来说就是,在源图像中寻找目标图像的位置

图像二值化

OpenCV中图像二值化的方法:

全局阈值

def treshold_demo(image):
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    ret, binarry = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
    print("threshold value %s" % ret)
    cv.imshow("binarry", binarry)

局部阈值
自适应阈值

def local_threshold(image):
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    binarry = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 25, 10)
    cv.imshow("binarry", binarry)

超大图像二值化: 如果图像过大,一些噪声或者光线就会影响二值化效果,所以通过分块后进行阈值化效果更好。

图像金字塔原理

reduce = 高斯模糊 + 降采样
expand = 扩大 + 卷积
降采样: PryDown
还原: PryUp
高斯金字塔与拉普拉斯金字塔
在使用拉普拉斯金字塔时注意,我们选用的图片大小必须是2^n大小,或者是一个宽高相等的图片

如何计算图像边缘?
1、对图像求一阶导数,导数最大的时候,图像的变化率最大,也就是图像的边缘。
2、对图像求二阶导数,二阶导数为0的时候,一阶导数最大,源图像变化率最大,也就是图像的边缘。
一阶导数与sobel算子(scharr算子效果更明显,但同时也会引入更多噪声)
二阶导数与拉普拉斯算子

Canny边缘提取

1、高斯模糊 - GaussianBlur
2、灰度转换 - cvtColor
3、计算梯度 - Sobel/Scharr
4、非最大信号抑制
5、高低阈值输出二值图像

直线检测

霍夫直线变换(Hough Line Transform)
cv.HoughLines
前提条件: 边缘检测已经完成
平面空间到极坐标空间转换

霍夫圆检测

霍夫圆变换原理

现实考量:

轮廓发现

轮廓发现:
是基于图像边缘提取的基础寻找对象的轮廓的方法。
所以边缘提取的阈值选定会影响最终轮廓发现结果。

API介绍:

如何利用梯度来避免阈值烦恼

对象检测

图像形态学

膨胀与腐蚀

开闭操作

其他形态学操作

分水岭算法

`

上一篇 下一篇

猜你喜欢

热点阅读