• 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)

  • 相关阅读:
    hdu 3507 Print Article —— 斜率优化DP
    bzoj 1096 仓库建设 —— 斜率优化DP
    ORDER BY 高级用法之CASE WHEN
    union和union all 的区别
    Ubuntu 链接ln的使用:创建和删除符号链接
    python中set和frozenset方法和区别
    python之sys模块详解
    odoo 8.0 多核启用
    Odoo 中的widget
    Odoo 在 Ubuntu 环境下性能调优
  • 原文地址:https://www.cnblogs.com/windyrainy/p/15169576.html
Copyright © 2020-2023  润新知