• Opencv图像处理


    图像处理

    1、灰度化

      灰度处理,就是把彩色的验证码图片转为灰色的图片。

      二值化,是将图片处理为只有黑白两色的图片,利于后面的图像处理和识别

      在OpenCV中有现成的方法可以进行灰度处理并二值化,处理后的效果:

    cvtcolor()函数是一个颜色空间转换函数。

    通道转换说明:
    经常用到的颜色转换:BGR-->RGB, BGR-->Gray 和 BGR-->HSV
    BGR-->RGB: 经过摄像头采集的图像的通道排列顺序为BGR,而BMP文件的排列顺序也为BGR,所以保存成BMP文件使不会出现什么问题。
    但是在显示器上显示的时候的排列顺序为RGB,所以RGB是为了让机器更好的显示图像,。
    BGR-->Gray:将BGR的图像转为单通道的灰色图像
    BGR-->HSV: 一般对颜色空间的图像进行有效处理都是在HSV空间进行的。RGB是为了让机器更好的显示图像,对于人类来说并不直观,
    HSV更为贴近我们的认知,所以通常我们在针对某种颜色做提取时会转换到HSV颜色空间里面来处理

    #读取原始图
    img = cv2.imread(img_path)
    cv2.imshow("img", img)
    
    # BGR-->RGB
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    cv2.imshow("img_rgb", img_rgb)
    
    # 图像的灰化YUV(Y为灰度图, Y=0.299R+0.587G+0.114B):BGR-- >Gray
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    cv2.imshow("img_gray", img_gray)
    
    # HSV模型转化YUV(Y为亮度, UV代表色度)BGR-->HSV
    img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    cv2.imshow("img_hsv", img_hsv)

    2、图像阈值

    对于每个像素,应用相同的阈值。如果像素值小于阈值,则将其设置为0,否则将其设置为最大值。

    cv.threshold()用来实现阈值分割,ret是return value缩写,代表当前的阈值,暂时不用理会。函数有4个参数:

    参数1:要处理的原图,一般是灰度图
    参数2:设定的阈值
    参数3:最大阈值,一般为255
    参数4:阈值的方式,主要有5种。

    0: THRESH_BINARY 当前点值大于阈值时,取Maxval,也就是第四个参数,下面再不说明,否则设置为0
    1: THRESH_BINARY_INV 当前点值大于阈值时,设置为0,否则设置为Maxval
    2: THRESH_TRUNC 当前点值大于阈值时,设置为阈值,否则不改变
    3: THRESH_TOZERO 当前点值大于阈值时,不改变,否则设置为0
    4: THRESH_TOZERO_INV 当前点值大于阈值时,设置为0,否则不改变

    # 应用5种不同的阈值方法
    ret, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
    ret, th2 = cv.threshold(img, 127, 255, cv.THRESH_BINARY_INV)
    ret, th3 = cv.threshold(img, 127, 255, cv.THRESH_TRUNC)
    ret, th4 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO)
    ret, th5 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO_INV)

    请通过类型的文档来观察区别。该方法返回两个输出。第一个是使用的阈值,第二个输出是阈值后的图像。此代码比较了不同的简单阈值类型:

    import cv2 as cv
    import numpy as np
    from matplotlib import pyplot as plt
    img = cv.imread('gradient.png',0)
    ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
    ret,thresh2 = cv.threshold(img,127,255,cv.THRESH_BINARY_INV)
    ret,thresh3 = cv.threshold(img,127,255,cv.THRESH_TRUNC)
    ret,thresh4 = cv.threshold(img,127,255,cv.THRESH_TOZERO)
    ret,thresh5 = cv.threshold(img,127,255,cv.THRESH_TOZERO_INV)
    titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
    images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
    for i in xrange(6):
        plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
        plt.title(titles[i])
        plt.xticks([]),plt.yticks([])
    plt.show()

    自适应阈值

    看得出来固定阈值是在整幅图片上应用一个阈值进行分割,它并不适用于明暗分布不均的图片。

    cv.adaptiveThreshold()自适应阈值会每次取图片的一小部分计算阈值,这样图片不同区域的阈值就不尽相同。它有5个参数:
    参数1:要处理的原图
    参数2:最大阈值,一般为255
    参数3:小区域阈值的计算方式
    ADAPTIVE_THRESH_MEAN_C:小区域内取均值
    ADAPTIVE_THRESH_GAUSSIAN_C:小区域内加权求和,权重是个高斯核
    参数4:阈值方式(跟前面讲的那5种相同)
    参数5:小区域的面积,如11就是11*11的小块
    参数6:最终阈值等于小区域计算出的阈值再减去此值
    # 固定阈值

    ret, th1= cv.threshold(img, 127, 255, cv.THRESH_BINARY)
    # 自适应阈值
    th2 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 15, 4)
    th3 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 15, 8)

    若我们使用一个全局值作为阈值,这可能并非在所有情况下都很好。例如,如果图像在不同区域具有不同的光照条件。在这种情况下,自适应阈值阈值化可以提供帮助。在此,算法基于像素周围的小区域确定像素的阈值。因此,对于同一图像的不同区域,我们获得了不同的阈值,这为光照度变化的图像提供了更好的结果。除上述参数外,方法cv.adaptiveThreshold还包含三个输入参数:该adaptiveMethod决定阈值是如何计算的:cv.ADAPTIVE_THRESH_MEAN_C::阈值是邻近区域的平均值减去常数C
    cv.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是邻域值的高斯加权总和减去常数C。该BLOCKSIZE确定附近区域的大小,C是从邻域像素的平均或加权总和中减去的一个常数。下面的代码比较了光照变化的图像的全局阈值和自适应阈值:

    结果:

    Otsu的二值化

    在全局阈值化中,我们使用任意选择的值作为阈值。相反,Otsu的方法避免了必须选择一个值并自动确定它的情况。考虑仅具有两个不同图像值的图像(双峰图像),其中直方图将仅包含两个峰。一个好的阈值应该在这两个值的中间。类似地,Otsu的方法从图像直方图中确定最佳全局阈值。为此,使用了cv.threshold作为附加标志传递。阈值可以任意选择。然后,算法找到最佳阈值,该阈值作为第一输出返回。查看以下示例。输入图像为噪点图像。在第一种情况下,采用值为127的全局阈值。在第二种情况下,直接采用Otsu阈值法。在第三种情况下,首先使用5x5高斯核对图像进行滤波以去除噪声,然后应用Otsu阈值处理。了解噪声滤波如何改善结果。

    import cv2 as cv
    import numpy as np
    from matplotlib import pyplot as plt
    img = cv.imread('noisy2.png',0)
    # 全局阈值
    ret1,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
    # Otsu阈值
    ret2,th2 = cv.threshold(img,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
    # 高斯滤波后再采用Otsu阈值
    blur = cv.GaussianBlur(img,(5,5),0)
    ret3,th3 = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
    # 绘制所有图像及其直方图
    images = [img, 0, th1,
              img, 0, th2,
              blur, 0, th3]
    titles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)',
              'Original Noisy Image','Histogram',"Otsu's Thresholding",
              'Gaussian filtered Image','Histogram',"Otsu's Thresholding"]
    for i in xrange(3):
        plt.subplot(3,3,i*3+1),plt.imshow(images[i*3],'gray')
        plt.title(titles[i*3]), plt.xticks([]), plt.yticks([])
        plt.subplot(3,3,i*3+2),plt.hist(images[i*3].ravel(),256)
        plt.title(titles[i*3+1]), plt.xticks([]), plt.yticks([])
        plt.subplot(3,3,i*3+3),plt.imshow(images[i*3+2],'gray')
        plt.title(titles[i*3+2]), plt.xticks([]), plt.yticks([])
    plt.show()

    Otsu的二值化如何实现?

    本节演示了Otsu二值化的Python实现,以展示其实际工作方式。如果您不感兴趣,可以跳过此步骤。由于我们正在处理双峰图像,因此Otsu的算法尝试找到一个阈值(t),该阈值将由关系式给出的加权类内方差最小化:

     实际上,它找到位于两个峰值之间的t值,以使两个类别的差异最小。它可以简单地在Python中实现,如下所示:

    img = cv.imread('noisy2.png',0)
    blur = cv.GaussianBlur(img,(5,5),0)
    # 寻找归一化直方图和对应的累积分布函数
    hist = cv.calcHist([blur],[0],None,[256],[0,256])
    hist_norm = hist.ravel()/hist.max()
    Q = hist_norm.cumsum()
    bins = np.arange(256)
    fn_min = np.inf
    thresh = -1
    for i in xrange(1,256):
        p1,p2 = np.hsplit(hist_norm,[i]) # 概率
        q1,q2 = Q[i],Q[255]-Q[i] # 对类求和
        b1,b2 = np.hsplit(bins,[i]) # 权重
        # 寻找均值和方差
        m1,m2 = np.sum(p1*b1)/q1, np.sum(p2*b2)/q2
        v1,v2 = np.sum(((b1-m1)**2)*p1)/q1,np.sum(((b2-m2)**2)*p2)/q2
        # 计算最小化函数
        fn = v1*q1 + v2*q2
        if fn < fn_min:
            fn_min = fn
            thresh = i
    # 使用OpenCV函数找到otsu的阈值
    ret, otsu = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
    print( "{} {}".format(thresh,ret) )

     大津二值化算法 ( Otsu's binarization ) ,是自动确定二值化图像时的阈值,也被称作最大类间方差法

    ret2,th2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

     THRESH_OTSU和THRESH_TRIANGLE
    这两种标志是获取阈值的方法,并不是阈值的比较方法的标志,这两个标志可以和前面5种标志一起使用。
    前面5种标志在调用函数时都需要人为的设置阈值,如果对图像不了解设置的阈值不合理,会对处理后的效果造成严重的影响,这两个标志分别表示利用大津法(OTSU)和三角形法(TRIANGLE)结合图像灰度值分布特性获取二值化的阈值,并将阈值以函数返回值的形式给出。

    help(cv2.cvtColor)

     灰度处理、二值化处理

    #导入模块
    import cv2
    
    #获取图片地址
    image_addr = 'D:\\Learning\\test.jpg'
    
    #读取图片,返回的是图片的一个数组对象
    image = cv2.imread(image_addr)
    #image对象常用属性如下:
    image_attr =[
    'all', 'any', 'argmax', 'argmin', 'argpartition', 'argsort', 'astype', 'base', 'byteswap', 'choose', 'clip', 'compress', 'conj',
    'conjugate', 'copy', 'ctypes','cumprod', 'cumsum', 'data', 'diagonal', 'dot', 'dtype', 'dump', 'dumps', 'fill', 'flags', 'flat',
    'flatten', 'getfield', 'imag', 'item', 'itemset', 'itemsize', 'max', 'mean', 'min', 'nbytes', 'ndim', 'newbyteorder', 'nonzero',
    'partition', 'prod', 'ptp', 'put', 'ravel', 'real', 'repeat', 'reshape', 'resize', 'round', 'searchsorted', 'setfield', 'setflags',
    'shape', 'size', 'sort', 'squeeze', 'std', 'strides', 'sum', 'swapaxes', 'take', 'tobytes', 'tofile', 'tolist', 'tostring', 'trace',
    'transpose', 'var', 'view']
    
    print(f"获取图片的维度: {image.ndim}")       #获取图片的维度
    print(f"获取图片的形状: {image.shape}")      #获取图片的形状
    print(f"获取图片的大小: {image.size}")       #获取图片的大小
    
    #对图片灰度处理
    image_cvt = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    #输出图片地址
    image_out_addr = 'D:\\Learning\\imagecvt.jpg'
    #输出图片
    cv2.imwrite(image_out_addr,image_cvt)
    
    #对灰度后的图片进行二值化处理
    image_binary = cv2.adaptiveThreshold(image_cvt, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 21, 1)
    #输出图片地址
    image_binary_addr = 'D:\\Learning\\binary.jpg'
    #输出图片
    cv2.imwrite(image_binary_addr,image_binary)

    3、模糊

    平均滤波cv2.Blur,

    高斯滤波cv2.GaussianBlur(),

    中值模糊cv2.medianBlur(),

    双边模糊cv2.bilateralFilter()

    4、去噪

    OpenCV提供了这种技术的四种变体。
    cv2.fastNlMeansDenoising() - 使用单个灰度图像
    cv2.fastNlMeansDenoisingColored() - 使用彩色图像。
    cv2.fastNlMeansDenoisingMulti() - 用于在短时间内捕获的图像序列(灰度图像)
    cv2.fastNlMeansDenoisingColoredMulti() - 与上面相同,但用于彩色图像。

    fastNlMeansDenoising(src[, dst[, h[, templateWindowSize[, searchWindowSize]]]])

    fastNlMeansDenoisingColored(src[, dst[, h[, hColor[, templateWindowSize[, searchWindowSize]]]]]) 

    fastNlMeansDenoisingMulti(srcImgs, imgToDenoiseIndex, temporalWindowSize[, dst[, h[, templateWindowSize[, searchWindowSize]]]])

    fastNlMeansDenoisingColoredMulti(srcImgs, imgToDenoiseIndex, temporalWindowSize[, dst[, h[, hColor[, templateWindowSize[, searchWindowSize]]]]])

    参数解析:
      h:参数决定滤波器强度。较高的h值可以更好地消除噪声,但也会删除图像的细节 (10 is ok)
      hColor:与h相同,但仅适用于彩色图像。 (通常与h相同)
      templateWindowSize:应该是奇数。 (recommended 7)
      searchWindowSize:应该是奇数。 (recommended 21)

    5、腐蚀和膨胀

    数学形态学提供了一组有用的方法,能够用来调整分割区域的形状以获得比较理想的结果,它最初是从数学中的集合论发展而来并用于处理二值图的,虽然运算很简单,但是往往可以产生很好的效果,后来这些方法推广到普通的灰度级图像处理中。常用的形态学处理方法包括:腐蚀、膨胀、开运算、闭运算、顶帽运算、底帽运算,其中膨胀与腐蚀是图像处理中最常用的形态学操作手段,其他方法是两者相互组合而产生的。

    形态学处理的核心就是定义结构元素,在OpenCV-Python中,可以使用其自带的getStructuringElement函数,也可以直接使用NumPy的ndarray来定义一个结构元素。

    getStructuringElement(shape, ksize[, anchor]) 

    参数解析:

      shape:核的形状
        MORPH_RECT 表示产生矩形的结构元
        MORPH_ELLIPSE 表示产生椭圆形的结构元
        MORPH_CROSS 表示产生十字交叉形的结构元
      ksize:表示结构元的尺寸,即(宽,高),必须是奇数
      anchor:表示结构元的锚点,即参考点。默认值Point(-1, -1)代表中心像素为锚点

    erode(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])把白色变成黑色

    参数解析

      src:表示输入矩阵
      kernel:表示结构元,即 函数getStructuringElement( )的返回值
      anchor:结构元的锚点,即参考点
      iterations:腐蚀操作的次数,默认为一次
      borderType:边界扩充类型
      borderValue:边界扩充值

    dilate(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])把黑色变成白色

    参数解析

      src:表示输入矩阵
      kernel:表示结构元,即 函数getStructuringElement( )的返回值
      anchor:结构元的锚点,即参考点
      iterations:膨胀操作的次数,默认为一次
      borderType:边界扩充类型
      borderValue:边界扩充值

    6、边缘检测

    cv2.Canny(), cv2.Laplacian(),cv2.Sobel()

    7、轮廓检测

     轮廓检测也是图像处理中经常用到的。OpenCV-Python接口中使用cv2.findContours()函数来查找检测物体的轮廓。

    import cv2  
    img = cv2.imread("./test.jpg")  
     
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  
    ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)  
    contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)  
    cv2.drawContours(img,contours,-1,(0,0,255),3)  
    cv2.imshow("img", img)  
    cv2.waitKey(0)  

    原图如下:

     检测结果如下:

     注意,findcontours函数会“原地”修改输入的图像。这一点可通过下面的语句验证:

    cv2.imshow("binary", binary)  
    contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)  
    cv2.imshow("binary2", binary)
    dn.net/hjxu2016/article/details/77833336

    执行这些语句后会发现原图被修改了。

    cv2.findContours()函数
    函数的原型为

    cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])  

    opencv2返回两个值:contours:hierarchy。注:opencv3会返回三个值,分别是img, countours, hierarchy。注意,findcontours函数会“原地”修改输入的图像。

    参数
    第一个参数是寻找轮廓的图像;
    第二个参数表示轮廓的检索模式,有四种(本文介绍的都是新的cv2接口):
      cv2.RETR_EXTERNAL 表示只检测外轮廓
      cv2.RETR_LIST 检测的轮廓不建立等级关系
      cv2.RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
      cv2.RETR_TREE 建立一个等级树结构的轮廓。

    第三个参数method为轮廓的近似办法
      cv2.CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
      cv2.CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
      cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS 使用teh-Chinl chain 近似算法

    返回值
    cv2.findContours()函数返回两个值,一个是轮廓本身,还有一个是每条轮廓对应的属性。

    contour返回值
    cv2.findContours()函数首先返回一个list,list中每个元素都是图像中的一个轮廓,用numpy中的ndarray表示。这个概念非常重要。在下面drawContours中会看见。通过

    print (type(contours))  
    print (type(contours[0]))  
    print (len(contours)) 

    可以验证上述信息。会看到本例中有两条轮廓,一个是五角星的,一个是矩形的。每个轮廓是一个ndarray,每个ndarray是轮廓上的点的集合。

    由于我们知道返回的轮廓有两个,因此可通过

    cv2.drawContours(img,contours,0,(0,0,255),3)  

    cv2.drawContours(img,contours,1,(0,255,0),3)  

    hierarchy返回值
    此外,该函数还可返回一个可选的hiararchy结果,这是一个ndarray,其中的元素个数和轮廓个数相同,每个轮廓contours[i]对应4个hierarchy元素hierarchy[i][0] ~hierarchy[i][3],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,则该值为负数。

    print (type(hierarchy))  
    print (hierarchy.ndim)  
    print (hierarchy[0].ndim)  
    print (hierarchy.shape)  

    可以看出,hierarchy本身包含两个ndarray,每个ndarray对应一个轮廓,每个轮廓有四个属性。

    8、绘画轮廓

    OpenCV中通过cv2.drawContours在图像上绘制轮廓。

    cv2.drawContours()函数

    cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset ]]]]]) 

    第一个参数是指明在哪幅图像上绘制轮廓;
    第二个参数是轮廓本身,在Python中是一个list。
    第三个参数指定绘制轮廓list中的哪条轮廓,如果是-1,则绘制其中的所有轮廓。后面的参数很简单。其中thickness表明轮廓线的宽度,如果是-1(cv2.FILLED),则为填充模式。绘制参数将在以后独立详细介绍。

    9、特征检测

    角点检测---cv2.cornerHarris(gray,2,3,0.04),Shi-Tomasi 角点检测器cv2.goodFeaturesToTrack(gray,20,0.01,10),尺度不变特征变换(SIFT),加速鲁棒特征(SURF)

  • 相关阅读:
    动态规划——Best Time to Buy and Sell Stock IV
    动态规划——Split Array Largest Sum
    动态规划——Burst Ballons
    动态规划——Best Time to Buy and Sell Stock III
    动态规划——Edit Distance
    动态规划——Longest Valid Parentheses
    动态规划——Valid Permutations for DI Sequence
    构建之法阅读笔记05
    构建之法阅读笔记04
    构建之法阅读笔记03
  • 原文地址:https://www.cnblogs.com/windyrainy/p/15169576.html
Copyright © 2020-2023  润新知