Opencv图像处理(全)

(一)银行卡号识别 —— sort_contours()、resize()

【信用卡检测流程详解】
11、提取模板的每个数字
1111、读取模板图像、转换成灰度图、转换成二值图
1122、轮廓检测、绘制轮廓、对得到的所有轮廓进行排序(编号)
1133、提取模板的所有轮廓 – 每一个数字
22、提取信用卡的所有轮廓
2211、读取待检测图像、转换为灰度图、顶帽操作、sobel算子操作、闭操作、二值化、二次膨胀+腐蚀
2222、轮廓检测、绘制轮廓
33、提取银行卡《四个数字一组》轮廓,然后每个轮廓与模板的每一个数字进行匹配,得到最大匹配结果
3311、在所有轮廓中,识别出《四个数字一组》的轮廓(共有四个),并进行阈值化、轮廓检测和轮廓排序
3322、在《四个数字一组》中,提取每个数字的轮廓以及坐标,并进行模板匹配得到最大匹配结果
44、在原图像上,用矩形画出《四个数字一组》,并在原图上显示所有的匹配结果

Opencv图像处理(全)
Opencv图像处理(全)
Opencv图像处理(全)
import cv2
import matplotlib.pyplot as plt
import numpy as np

def sort_contours(cnt_s, method="left-to-right"):
    reverse = False
    ii_myutils = 0
    if method == "right-to-left" or method == "bottom-to-top":
        reverse = True
    if method == "top-to-bottom" or method == "bottom-to-top":
        ii_myutils = 1
    bounding_boxes = [cv2.boundingRect(cc_myutils) for cc_myutils in cnt_s]
    (cnt_s, bounding_boxes) = zip(*sorted(zip(cnt_s, bounding_boxes), key=lambda b: b[1][ii_myutils], reverse=reverse))
    return cnt_s, bounding_boxes

def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
    dim_myutils = None
    (h_myutils, w_myutils) = image.shape[:2]
    if width is None and height is None:
        return image
    if width is None:
        r_myutils = height / float(h_myutils)
        dim_myutils = (int(w_myutils * r_myutils), height)
    else:
        r_myutils = width / float(w_myutils)
        dim_myutils = (width, int(h_myutils * r_myutils))
    resized = cv2.resize(image, dim_myutils, interpolation=inter)
    return resized

img = cv2.imread(r'ocr_a_reference.png')
ref_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ref_BINARY = cv2.threshold(ref_gray, 10, 255, cv2.THRESH_BINARY_INV)[1]
"""#######################################
轮廓检测:contours, hierarchy = cv2.findContours(img, mode, method)
输入参数      mode: 轮廓检索模式
                  (1)RETR_EXTERNAL:  只检索最外面的轮廓;
                  (2)RETR_LIST:      检索所有的轮廓,但检测的轮廓不建立等级关系,将其保存到一条链表当中,
                  (3)RETR_CCOMP:     检索所有的轮廓,并建立两个等级的轮廓。顶层是各部分的外部边界,内层是的边界信息;
                  (4)RETR_TREE:      检索所有的轮廓,并建立一个等级树结构的轮廓;(最常用)
            method: 轮廓逼近方法
                  (1)CHAIN_APPROX_NONE:      存储所有的轮廓点,相邻的两个点的像素位置差不超过1。               例如:矩阵的四条边。(最常用)
                  (2)CHAIN_APPROX_SIMPLE:     压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标。   例如:矩形的4个轮廓点。
输出参数     contours:所有的轮廓
            hierarchy:每条轮廓对应的属性
备注0:轮廓就是将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。
备注1:函数输入图像是二值图,即黑白的(不是灰度图)。所以读取的图像要先转成灰度的,再转成二值图。
备注2:函数在opencv2只返回两个值:contours, hierarchy。
备注3:函数在opencv3会返回三个值:img, countours, hierarchy
#######################################"""
refCnts, hierarchy = cv2.findContours(ref_BINARY.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

"""#######################################
绘制轮廓:v2.drawContours(image, contours, contourIdx, color, thickness) ———— (在图像上)画出图像的轮廓
输入参数        image:             需要绘制轮廓的目标图像,注意会改变原图
              contours:           轮廓点,上述函数cv2.findContours()的第一个返回值
              contourIdx:         轮廓的索引,表示绘制第几个轮廓。-1表示绘制所有的轮廓
              color:              绘制轮廓的颜色(RGB)
              thickness:          (可选参数)轮廓线的宽度,-1表示填充
备注:图像需要先复制一份copy(), 否则(赋值操作的图像)与原图会随之一起改变。
#######################################"""
img_Contours = img.copy()
cv2.drawContours(img_Contours, refCnts, -1, (0, 0, 255), 3)

plt.subplot(221),   plt.imshow(img, 'gray'),            plt.title('(0)ref')
plt.subplot(222),   plt.imshow(ref_gray, 'gray'),       plt.title('(1)ref_gray')
plt.subplot(223),   plt.imshow(ref_BINARY, 'gray'),     plt.title('(2)ref_BINARY')
plt.subplot(224),   plt.imshow(img_Contours, 'gray'),   plt.title('(3)img_Contours')
plt.show()

refCnts = sort_contours(refCnts, method="left-to-right")[0]

digits = {}
for (i, c) in enumerate(refCnts):
    (x, y, w, h) = cv2.boundingRect(c)
    roi = ref_BINARY[y:y + h, x:x + w]
    roi = cv2.resize(roi, (57, 88))
    digits[i] = roi

"""######################################################################
初始化卷积核:getStructuringElement(shape, ksize)
输入参数:       shape:形状
                        (1) MORPH_RECT          矩形
                        (2) MORPH_CROSS         十字型
                        (3) MORPH_ELLIPSE       椭圆形
                ksize:卷积核大小。例如:(3, 3)表示3*3的卷积核
######################################"""
rect_Kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
square_Kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))

image_card = cv2.imread(r'images\credit_card_01.png')
image_resize = resize(image_card, width=300)
image_gray = cv2.cvtColor(image_resize, cv2.COLOR_BGR2GRAY)

"""#######################################
形态学变化函数:cv2.morphologyEx(src, op, kernel)
参数说明:src传入的图片,op进行变化的方式, kernel表示方框的大小
op变化的方式有五种:
        开运算(open):             cv2.MORPH_OPEN              先腐蚀,再膨胀。                  开运算可以用来消除小黑点。
        闭运算(close):            cv2.MORPH_CLOSE             先膨胀,再腐蚀。                  闭运算可以用来突出边缘特征。
        形态学梯度(morph-grad):    cv2.MORPH_GRADIENT          膨胀后图像(减去)腐蚀图像。        可以突出团块(blob)的边缘,保留物体的边缘轮廓。
        顶帽(top-hat):            cv2.MORPH_TOPHAT            原始输入(减去)开运算结果。        将突出比原轮廓亮的部分。
        黑帽(black-hat):          cv2.MORPH_BLACKHAT          闭运算结果(减去)原始输入          将突出比原轮廓暗的部分。
#######################################"""
image_tophat = cv2.morphologyEx(image_gray, cv2.MORPH_TOPHAT, rect_Kernel)

"""#######################################
Sobel算子是一种常用的边缘检测算子。对噪声具有平滑作用,提供较为精确的边缘方向信息,但是边缘定位精度不够高。
        边缘就是像素对应的灰度值快速变化的地方。如:黑到白的边界
        图像是二维的。Sobel算子在x,y两个方向求导,故有不同的两个卷积核(Gx, Gy),且Gx的转置等于Gy。分别反映了每一点像素在水平方向和在垂直方向上的亮度变换情况.

########################################
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
输入参数    src        输入图像
          ddepth     图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度;
          dx和dy     表示的是求导的阶数,0表示这个方向上没有求导,一般为0、1、2。
          ksize      卷积核大小,一般为3、5。
同时对x和y进行求导,会导致部分信息丢失。(不建议)- 分别计算x和y,再求和(效果好)
########################################
(1)cv2.CV_16S的说明
      (1)Sobel函数求完导数后会有负值,还有会大于255的值。
      (2)而原图像是uint8,即8位无符号数。所以Sobel建立图像的位数不够,会有截断。
      (3)因此要使用16位有符号的数据类型,即cv2.CV_16S。
(2)cv2.convertScaleAbs(): 给图像的所有像素加一个绝对值
      通过该函数将其转回原来的uint8形式。否则将无法显示图像,而只是一副灰色的窗口。
########################################"""

image_gradx = cv2.Sobel(image_tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
image_gradx = np.absolute(image_gradx)
(minVal, maxVal) = (np.min(image_gradx), np.max(image_gradx))
image_gradx = (255 * ((image_gradx - minVal) / (maxVal - minVal)))
image_gradx = image_gradx.astype("uint8")
"""
sobel_Gx1 = cv2.Sobel(image_tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=3)     # 3*3卷积核
sobel_Gx_Abs1 = cv2.convertScaleAbs(sobel_Gx1)                                  # (1)左边减右边(2)白到黑是正数,黑到白就是负数,且所有的负数会被截断成0,所以要取绝对值。
sobel_Gy1 = cv2.Sobel(image_tophat, cv2.CV_64F, 0, 1, ksize=3)
sobel_Gy_Abs1 = cv2.convertScaleAbs(sobel_Gy1)
sobel_Gx_Gy_Abs1 = cv2.addWeighted(sobel_Gx_Abs1, 0.5, sobel_Gy_Abs1, 0.5, 0)   # 权重值x + 权重值y +偏置b
"""

image_CLOSE = cv2.morphologyEx(image_gradx, cv2.MORPH_CLOSE, square_Kernel)
"""########################################
图像阈值 ret, dst = cv2.threshold(src, thresh, max_val, type)
输入参数        dst:    输出图
                src:    输入图,只能输入单通道图像,通常来说为灰度图
                thresh:     阈值
                max_val:    当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
                type:       二值化操作的类型,包含以下5种类型:
                    (1) cv2.THRESH_BINARY             超过阈值部分取max_val(最大值),否则取0
                  (2) cv2.THRESH_BINARY_INV         THRESH_BINARY的反转
                    (3) cv2.THRESH_TRUNC              大于阈值部分设为阈值,否则不变
                    (4) cv2.THRESH_TOZERO             大于阈值部分不改变,否则设为0
                    (5) cv2.THRESH_TOZERO_INV         THRESH_TOZERO的反转
########################################"""

image_thresh = cv2.threshold(image_CLOSE, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

image_2_dilate = cv2.dilate(image_thresh, square_Kernel, iterations=2)
image_1_erode = cv2.erode(image_2_dilate, square_Kernel, iterations=1)
image_2_CLOSE = image_1_erode

threshCnts, hierarchy = cv2.findContours(image_2_CLOSE.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

image_Contours = image_resize.copy()
cv2.drawContours(image_Contours, threshCnts, -1, (0, 0, 255), 3)

plt.subplot(241),   plt.imshow(image_card, 'gray'),             plt.title('(0)image_card')
plt.subplot(242),   plt.imshow(image_gray, 'gray'),             plt.title('(1)image_gray')
plt.subplot(243),   plt.imshow(image_tophat, 'gray'),           plt.title('(2)image_tophat')
plt.subplot(244),   plt.imshow(image_gradx, 'gray'),            plt.title('(3)image_gradx')
plt.subplot(245),   plt.imshow(image_CLOSE, 'gray'),            plt.title('(4)image_CLOSE')
plt.subplot(246),   plt.imshow(image_thresh, 'gray'),           plt.title('(5)image_thresh')
plt.subplot(247),   plt.imshow(image_2_CLOSE, 'gray'),          plt.title('(6)image_2_CLOSE')
plt.subplot(248),   plt.imshow(image_Contours, 'gray'),         plt.title('(7)image_Contours')
plt.show()

locs = []
for (i, c) in enumerate(threshCnts):
    (x, y, w, h) = cv2.boundingRect(c)
    ar = w / float(h)

    if (2.0 < ar and ar < 4.0):
        if (35 < w < 60) and (10 < h < 20):
            locs.append((x, y, w, h))

locs = sorted(locs, key=lambda x:x[0])

output = []

for (ii, (gX, gY, gW, gH)) in enumerate(locs):
    groupOutput = []

    group_digit = image_gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]
    group_digit_th = cv2.threshold(group_digit, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    digitCnts, hierarchy = cv2.findContours(group_digit_th.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    digitCnts = sort_contours(digitCnts, method="left-to-right")[0]

    for jj in digitCnts:
        (x, y, w, h) = cv2.boundingRect(jj)
        roi = group_digit[y:y + h, x:x + w]
        roi = cv2.resize(roi, (57, 88))
        cv2.imshow("Image", roi)
        cv2.waitKey(200)
        """########################################
        # 模板匹配:cv2.matchTemplate(image, template, method)
        # 输入参数      image 待检测图像
        #               template 模板图像
        #               method 模板匹配方法:
        #                       (1)cv2.TM_SQDIFF:         计算平方差。          计算出来的值越接近0,越相关
        #                       (2)cv2.TM_CCORR:          计算相关性。          计算出来的值越大,越相关
        #                       (3)cv2.TM_CCOEFF:         计算相关系数。         计算出来的值越大,越相关
        #                       (4)cv2.TM_SQDIFF_NORMED:  计算(归一化)平方差。   计算出来的值越接近0,越相关
        #                       (5)cv2.TM_CCORR_NORMED:   计算(归一化)相关性。   计算出来的值越接近1,越相关
        #                       (6)cv2.TM_CCOEFF_NORMED:  计算(归一化)相关系数。  计算出来的值越接近1,越相关
        # (最好选择有归一化操作,效果好)
        ########################################
        # 获取匹配结果函数:min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(ret)
        # 其中: ret是cv2.matchTemplate函数返回的矩阵;
        #       min_val, max_val, min_loc, max_loc分别表示最小值,最大值,最小值与最大值在图像中的位置
        # 如果模板方法是平方差或者归一化平方差,要用min_loc; 其余用max_loc
        ########################################"""
        scores = []
        for (kk, digitROI) in digits.items():
            result = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF)
            (_, max_score, _, _) = cv2.minMaxLoc(result)
            scores.append(max_score)
        groupOutput.append(str(np.argmax(scores)))
    """########################################
    # 添加文字及修改格式函数:cv2.putText(img, str(i), (123, 456)), font, 2, (0, 255, 0), 3)
    #                    输入参数依次是:图片,添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细
    ########################################"""

    cv2.rectangle(image_resize, (gX - 5, gY - 5), (gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)
    cv2.putText(image_resize, "".join(groupOutput), (gX, gY - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)

    output.extend(groupOutput)

cv2.imshow("Image", image_resize)
cv2.waitKey(0)

(二)文档扫描OCR识别 —— cv2.getPerspectiveTransform() + cv2.warpPerspective()、np.argmin()、np.argmax()、np.diff()

计算轮廓的长度:cv2.arcLength(curve, closed)
找出轮廓的多边形拟合曲线:approxPolyDP(contourMat, 10, true)
求最小值对应的索引:np.argmin()
求最大值对应的索引:np.argmax()
求(同一行)列与列之间的差值:np.diff()

Opencv图像处理(全)
import numpy as np
import cv2
import matplotlib.pyplot as plt
"""######################################################################
计算齐次变换矩阵:cv2.getPerspectiveTransform(rect, dst)
输入参数        rect输入图像的四个点(四个角)
                dst输出图像的四个点(方方正正的图像对应的四个角)
######################################################################
仿射变换:cv2.warpPerspective(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)
透视变换:cv2.warpAffine(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)
                src:输入图像     dst:输出图像
                M:2×3的变换矩阵
                dsize:变换后输出图像尺寸
                flag:插值方法
                borderMode:边界像素外扩方式
                borderValue:边界像素插值,默认用0填充
#
(Affine Transformation)可实现旋转,平移,缩放,变换后的平行线依旧平行。
(Perspective Transformation)即以不同视角的同一物体,在像素坐标系中的变换,可保持直线不变形,但是平行线可能不再平行。
#
备注:cv2.warpAffine需要与cv2.getPerspectiveTransform搭配使用。
######################################################################"""

def order_points(pts):
    rect = np.zeros((4, 2), dtype="float32")

    s = pts.sum(axis=1)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]

    diff = np.diff(pts, axis=1)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]
    return rect

def four_point_transform(image, pts):
    rect = order_points(pts)
    (tl, tr, br, bl) = rect

    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
    maxWidth = max(int(widthA), int(widthB))

    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int(heightA), int(heightB))

    dst = np.array([[0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]], dtype="float32")
    """###############################################################################
    # 计算齐次变换矩阵:cv2.getPerspectiveTransform(rect, dst)
    ###############################################################################"""
    M = cv2.getPerspectiveTransform(rect, dst)

    """###############################################################################
    # 透视变换(将输入矩形乘以(齐次变换矩阵),得到输出矩阵)
    ###############################################################################"""
    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
    return warped

def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
    dim = None
    (h, w) = image.shape[:2]
    if width is None and height is None:
        return image
    if width is None:
        r = height / float(h)
        dim = (int(w * r), height)
    else:
        r = width / float(w)
        dim = (width, int(h * r))
    resized = cv2.resize(image, dim, interpolation=inter)
    return resized

image = cv2.imread(r'images\receipt.jpg')
ratio = image.shape[0] / 500.0
orig = image.copy()
image = resize(orig, height=500)

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)

print("STEP 1: 边缘检测")
cv2.imshow("Image", image)
cv2.imshow("Edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()

cnts, hierarchy = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]
for c in cnts:
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)
    if len(approx) == 4:
        screenCnt = approx
        break

print("STEP 2: 获取轮廓")
cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
cv2.imshow("Outline", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
ref = cv2.threshold(warped, 100, 255, cv2.THRESH_BINARY)[1]
ref = resize(ref, height=500)

print("STEP 3: 齐次变换")
cv2.imshow("Scanned", ref)
cv2.waitKey(0)
cv2.destroyAllWindows()

orig = cv2.cvtColor(orig, cv2.COLOR_BGR2RGB)
edged = cv2.cvtColor(edged, cv2.COLOR_BGR2RGB)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
ref = cv2.cvtColor(ref, cv2.COLOR_BGR2RGB)

plt.subplot(2, 2, 1),    plt.imshow(orig),      plt.title('orig')
plt.subplot(2, 2, 2),    plt.imshow(edged),     plt.title('edged')
plt.subplot(2, 2, 3),    plt.imshow(image),     plt.title('contour')
plt.subplot(2, 2, 4),    plt.imshow(ref),       plt.title('rectangle')
plt.show()

"""######################################################################
计算轮廓的长度:retval = cv2.arcLength(curve, closed)
输入参数:      curve              轮廓(曲线)。
               closed             若为true,表示轮廓是封闭的;若为false,则表示打开的。(布尔类型)
输出参数:      retval             轮廓的长度(周长)。
######################################################################
找出轮廓的多边形拟合曲线:approxCurve = approxPolyDP(contourMat, 10, true)
输入参数:     contourMat:              轮廓点矩阵(集合)
              epsilon:                 (double类型)指定的精度, 即原始曲线与近似曲线之间的最大距离。
              closed:                  (bool类型)若为true, 则说明近似曲线是闭合的; 反之, 若为false, 则断开。
输出参数:     approxCurve:             轮廓点矩阵(集合);当前点集是能最小包容指定点集的。画出来即是一个多边形;
######################################################################"""

(三)全景拼接 —— detectAndDescribe()、matchKeypoints()、cv2.findHomography()、cv2.warpPerspective()、drawMatches()

函数功能:利用sift算法,实现全景拼接算法,将给定的两幅图片拼接为一幅.
11:从输入的两张图片里检测关键点、提取(sift)局部不变特征。
22:匹配的两幅图像之间的特征(Lowe’s算法:比较最近邻距离与次近邻距离)
33:使用RANSAC算法(随机抽样一致算法),利用匹配特征向量估计单映射变换矩阵(homography:单应性)。
44:利用33得到的单映矩阵应用透视变换。

Opencv图像处理(全)
import cv2
import numpy as np
"""#########################################################
预定义框架说明
定义一个Stitcher类:stitch()、detectAndDescribe()、matchKeypoints()、drawMatches()
          stitch()                拼接函数
          detectAndDescribe()     检测图像的SIFT关键特征点,并计算特征描述子
          matchKeypoints()        匹配两张图片的所有特征点
          cv2.findHomography()    计算单映射变换矩阵
          cv2.warpPerspective()   透视变换(作用:缝合图像)
          drawMatches()           建立直线关键点的匹配可视化
#
备注:cv2.warpPerspective()需要与cv2.findHomography()搭配使用。
#########################################################"""

class Stitcher:

    def stitch(self, images, ratio=0.75, reprojThresh=4.0, showMatches=False):
        (imageB, imageA) = images
        (kpsA, featuresA) = self.detectAndDescribe(imageA)
        (kpsB, featuresB) = self.detectAndDescribe(imageB)
        M = self.matchKeypoints(kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh)

        if M is None:
            return None

        (matches, H, status) = M
        result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
        result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB

        if showMatches:
            vis = self.drawMatches(imageA, imageB, kpsA, kpsB, matches, status)
            return (result, vis)

        return result

    def detectAndDescribe(self, image):

        descriptor = cv2.xfeatures2d.SIFT_create()
        """#####################################################
        # 如果是OpenCV3.X,则用cv2.xfeatures2d.SIFT_create方法来实现DoG关键点检测和SIFT特征提取。
        # 如果是OpenCV2.4,则用cv2.FeatureDetector_create方法来实现关键点的检测(DoG)。
        #####################################################"""
        (kps, features) = descriptor.detectAndCompute(image, None)
        kps = np.float32([kp.pt for kp in kps])

        return (kps, features)

    def matchKeypoints(self, kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh):
        matcher = cv2.BFMatcher()
        rawMatches = matcher.knnMatch(featuresA, featuresB, 2)

        matches = []
        for m in rawMatches:
            if len(m) == 2 and m[0].distance < m[1].distance * ratio:
                matches.append((m[0].trainIdx, m[0].queryIdx))

        if len(matches) > 4:

            ptsA = np.float32([kpsA[i] for (_, i) in matches])
            ptsB = np.float32([kpsB[i] for (i, _) in matches])
            (H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, reprojThresh)
            return (matches, H, status)

        return None

    def drawMatches(self, imageA, imageB, kpsA, kpsB, matches, status):
        (hA, wA) = imageA.shape[:2]
        (hB, wB) = imageB.shape[:2]
        vis = np.zeros((max(hA, hB), wA + wB, 3), dtype="uint8")
        vis[0:hA, 0:wA] = imageA
        vis[0:hB, wA:] = imageB

        for ((trainIdx, queryIdx), s) in zip(matches, status):
            if s == 1:
                ptA = (int(kpsA[queryIdx][0]), int(kpsA[queryIdx][1]))
                ptB = (int(kpsB[trainIdx][0]) + wA, int(kpsB[trainIdx][1]))
                cv2.line(vis, ptA, ptB, (0, 255, 0), 1)

        return vis

if __name__ == '__main__':

    imageA = cv2.imread("left_01.png")
    imageB = cv2.imread("right_01.png")

    stitcher = Stitcher()
    (result, vis) = stitcher.stitch([imageA, imageB], showMatches=True)

    cv2.imshow("Image A", imageA)
    cv2.imshow("Image B", imageB)
    cv2.imshow("Keypoint Matches", vis)
    cv2.imshow("Result", result)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

(四)停车场车位检测(基于Keras的CNN分类) —— pickle.dump()、pickle.load()、cv2.fillPoly()、cv2.bitwise_and()、cv2.circle()、cv2.HoughLinesP()、cv2.line()

该项目共分为三个py文件: Parking.py(定义所有的功能函数)、train.py(训练神经网络)、park_test.py(开始检测停车位状态)

Opencv图像处理(全)
Opencv图像处理(全)

(1)Parking.py


import matplotlib.pyplot as plt
import cv2
import os
import glob
import numpy as np

class Parking:

    def show_images(self, images, cmap=None):
        cols = 2
        rows = (len(images)+1)//cols

        plt.figure(figsize=(15, 12))
        for i, image in enumerate(images):
            plt.subplot(rows, cols, i+1)
            cmap = 'gray' if len(image.shape) == 2 else cmap
            plt.imshow(image, cmap=cmap)
            plt.xticks([])
            plt.yticks([])
        plt.tight_layout(pad=0, h_pad=0, w_pad=0)
        plt.show()

    def cv_show(self, name, img):
        cv2.imshow(name, img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    def select_rgb_white_yellow(self, image):

        lower = np.uint8([120, 120, 120])
        upper = np.uint8([255, 255, 255])

        white_mask = cv2.inRange(image, lower, upper)
        self.cv_show('white_mask', white_mask)

        masked = cv2.bitwise_and(image, image, mask=white_mask)
        self.cv_show('masked', masked)
        return masked

    def convert_gray_scale(self, image):
        return cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)

    def detect_edges(self, image, low_threshold=50, high_threshold=200):
        return cv2.Canny(image, low_threshold, high_threshold)

    def filter_region(self, image, vertices):

        mask = np.zeros_like(image)
        if len(mask.shape) == 2:

            cv2.fillPoly(mask, vertices, 255)
            self.cv_show('mask', mask)

        return cv2.bitwise_and(image, mask)

    def select_region(self, image):

        rows, cols = image.shape[:2]
        pt_1 = [cols*0.05, rows*0.90]
        pt_2 = [cols*0.05, rows*0.70]
        pt_3 = [cols*0.30, rows*0.55]
        pt_4 = [cols*0.6, rows*0.15]
        pt_5 = [cols*0.90, rows*0.15]
        pt_6 = [cols*0.90, rows*0.90]

        vertices = np.array([[pt_1, pt_2, pt_3, pt_4, pt_5, pt_6]], dtype=np.int32)
        point_img = image.copy()
        point_img = cv2.cvtColor(point_img, cv2.COLOR_GRAY2RGB)
        for point in vertices[0]:
            """#####################################################
            # 绘制圆型:cv2.circle(图像, 圆心, 半径, 颜色, 厚度)
            # 输入参数         (1)图像:在该图像上绘图。
            #                (2)圆心:圆的中心坐标。坐标表示为两个值的元组, 即(X坐标值, Y坐标值)。
            #                (3)半径:圆的半径。
            #                (4)颜色:圆的边界线颜色。对于BGR, 我们传递一个元组。例如:(255, 0, 0)为蓝色。
            #                (5)厚度:正数表示线的粗细。其中:-1表示实心圆。
            #####################################################"""
            cv2.circle(point_img, (point[0], point[1]), 10, (0, 0, 255), 4)
        self.cv_show('point_img', point_img)

        return self.filter_region(image, vertices)

    def hough_lines(self, image):
        """#####################################################
        # 检测图像中所有的线:cv2.HoughLinesP(image, rho=0.1, theta=np.pi / 10, threshold=15, minLineLength=9, maxLineGap=4)
        # image             输入的图像需要是边缘检测后的结果
        # minLineLengh      (线的最短长度,比这个短的都被忽略)
        # MaxLineCap        (两条直线之间的最大间隔,小于此值,认为是一条直线)
        # rho               距离精度
        # theta             角度精度
        # threshod          超过设定阈值才被检测出线段
        #####################################################"""
        return cv2.HoughLinesP(image, rho=0.1, theta=np.pi/10, threshold=15, minLineLength=9, maxLineGap=4)

    def draw_lines(self, image, lines, color=[255, 0, 0], thickness=2, make_copy=True):

        if make_copy:
            image = np.copy(image)
        cleaned = []
        for line in lines:
            for x1, y1, x2, y2 in line:

                if abs(y2-y1)  1 and abs(x2-x1) >= 25 and abs(x2-x1)  55:
                    cleaned.append((x1, y1, x2, y2))
                    cv2.line(image, (x1, y1), (x2, y2), color, thickness)
        print(" No lines detected: ", len(cleaned))
        return image

    def identify_blocks(self, image, lines, make_copy=True):

        if make_copy:
            new_image = np.copy(image)

        cleaned = []
        for line in lines:
            for x1, y1, x2, y2 in line:
                if abs(y2-y1) 1 and abs(x2-x1) >=25 and abs(x2-x1)  55:
                    cleaned.append((x1, y1, x2, y2))

        import operator
        list1 = sorted(cleaned, key=operator.itemgetter(0, 1))

        clusters = {}
        dIndex = 0
        clus_dist = 10
        for i in range(len(list1) - 1):
            distance = abs(list1[i+1][0] - list1[i][0])
            if distance  clus_dist:
                if not dIndex in clusters.keys(): clusters[dIndex] = []
                clusters[dIndex].append(list1[i])
                clusters[dIndex].append(list1[i + 1])
            else:
                dIndex += 1

        rects = {}
        i = 0
        for key in clusters:
            all_list = clusters[key]
            cleaned = list(set(all_list))
            if len(cleaned) > 5:
                cleaned = sorted(cleaned, key=lambda tup: tup[1])
                avg_y1 = cleaned[0][1]
                avg_y2 = cleaned[-1][1]
                avg_x1 = 0
                avg_x2 = 0
                for tup in cleaned:
                    avg_x1 += tup[0]
                    avg_x2 += tup[2]
                avg_x1 = avg_x1/len(cleaned)
                avg_x2 = avg_x2/len(cleaned)
                rects[i] = (avg_x1, avg_y1, avg_x2, avg_y2)
                i += 1
        print("Num Parking Lanes: ", len(rects))

        buff = 7
        for key in rects:
            tup_topLeft = (int(rects[key][0] - buff), int(rects[key][1]))
            tup_botRight = (int(rects[key][2] + buff), int(rects[key][3]))
            cv2.rectangle(new_image, tup_topLeft, tup_botRight, (0, 255, 0), 3)
        return new_image, rects

    def draw_parking(self, image, rects, make_copy=True, color=[255, 0, 0], thickness=2, save=True):
        if make_copy:
            new_image = np.copy(image)
        gap = 15.5
        spot_dict = {}
        tot_spots = 0

        adj_y1 = {0: 20, 1: -10, 2: 0, 3: -11, 4: 28, 5: 5, 6: -15, 7: -15, 8: -10, 9: -30, 10: 9, 11: -32}
        adj_y2 = {0: 30, 1: 50, 2: 15, 3: 10, 4: -15, 5: 15, 6: 15, 7: -20, 8: 15, 9: 15, 10: 0, 11: 30}

        adj_x1 = {0: -8, 1: -15, 2: -15, 3: -15, 4: -15, 5: -15, 6: -15, 7: -15, 8: -10, 9: -10, 10: -10, 11: 0}
        adj_x2 = {0: 0, 1: 15, 2: 15, 3: 15, 4: 15, 5: 15, 6: 15, 7: 15, 8: 10, 9: 10, 10: 10, 11: 0}
        for key in rects:
            tup = rects[key]
            x1 = int(tup[0] + adj_x1[key])
            x2 = int(tup[2] + adj_x2[key])
            y1 = int(tup[1] + adj_y1[key])
            y2 = int(tup[3] + adj_y2[key])
            cv2.rectangle(new_image, (x1, y1), (x2, y2), (0, 255, 0), 2)

            num_splits = int(abs(y2-y1)//gap)
            for i in range(0, num_splits+1):
                y = int(y1 + i*gap)

                cv2.line(new_image, (x1, y), (x2, y), color, thickness)
            if 0 < key < len(rects) - 1:

                x = int((x1 + x2)/2)
                cv2.line(new_image, (x, y1), (x, y2), color, thickness)

            if key == 0 or key == (len(rects) - 1):
                tot_spots += num_splits + 1
            else:
                tot_spots += 2*(num_splits + 1)

            if key == 0 or key == (len(rects) - 1):
                for i in range(0, num_splits+1):
                    cur_len = len(spot_dict)
                    y = int(y1 + i*gap)
                    spot_dict[(x1, y, x2, y+gap)] = cur_len + 1
            else:
                for i in range(0, num_splits+1):
                    cur_len = len(spot_dict)
                    y = int(y1 + i*gap)
                    x = int((x1 + x2)/2)
                    spot_dict[(x1, y, x, y+gap)] = cur_len + 1
                    spot_dict[(x, y, x2, y+gap)] = cur_len + 2
        print("total parking spaces: ", tot_spots, cur_len)
        if save:
            filename = 'with_parking.jpg'
            cv2.imwrite(filename, new_image)
        return new_image, spot_dict

    def assign_spots_map(self, image, spot_dict, make_copy=True, color=[255, 0, 0], thickness=2):
        if make_copy:
            new_image = np.copy(image)
        for spot in spot_dict.keys():
            (x1, y1, x2, y2) = spot
            cv2.rectangle(new_image, (int(x1), int(y1)), (int(x2), int(y2)), color, thickness)
        return new_image

    def save_images_for_cnn(self, image, spot_dict, folder_name='cnn_data'):

        for spot in spot_dict.keys():
            (x1, y1, x2, y2) = spot
            (x1, y1, x2, y2) = (int(x1), int(y1), int(x2), int(y2))

            spot_img = image[y1:y2, x1:x2]
            spot_img = cv2.resize(spot_img, (0, 0), fx=2.0, fy=2.0)
            spot_id = spot_dict[spot]

            filename = 'spot' + str(spot_id) + '.jpg'
            print(spot_img.shape, filename, (x1, x2, y1, y2))
            cv2.imwrite(os.path.join(folder_name, filename), spot_img)

    def make_prediction(self, image, model, class_dictionary):

        img = image/255.

        image = np.expand_dims(img, axis=0)

        class_predicted = model.predict(image)
        inID = np.argmax(class_predicted[0])
        label = class_dictionary[inID]
        return label

    def predict_on_image(self,image, spot_dict , model,class_dictionary,make_copy=True, color=[0, 255, 0], alpha=0.5):
        if make_copy:
            new_image = np.copy(image)
            overlay = np.copy(image)
        self.cv_show('new_image', new_image)
        cnt_empty = 0
        all_spots = 0
        for spot in spot_dict.keys():
            all_spots += 1
            (x1, y1, x2, y2) = spot
            (x1, y1, x2, y2) = (int(x1), int(y1), int(x2), int(y2))
            spot_img = image[y1:y2, x1:x2]
            spot_img = cv2.resize(spot_img, (48, 48))

            label = self.make_prediction(spot_img,model,class_dictionary)
            if label == 'empty':
                cv2.rectangle(overlay, (int(x1), int(y1)), (int(x2), int(y2)), color, -1)
                cnt_empty += 1
        cv2.addWeighted(overlay, alpha, new_image, 1 - alpha, 0, new_image)
        cv2.putText(new_image, "Available: %d spots" % cnt_empty, (30, 95), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
        cv2.putText(new_image, "Total: %d spots" % all_spots, (30, 125), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
        save = False

        if save:
            filename = 'with_marking.jpg'
            cv2.imwrite(filename, new_image)
        self.cv_show('new_image', new_image)
        return new_image

    def predict_on_video(self, video_name, final_spot_dict, model, class_dictionary, ret=True):
        cap = cv2.VideoCapture(video_name)
        count = 0
        while ret:
            ret, image = cap.read()
            count += 1
            if count == 5:
                count = 0

                new_image = np.copy(image)
                overlay = np.copy(image)
                cnt_empty = 0
                all_spots = 0
                color = [0, 255, 0]
                alpha = 0.5
                for spot in final_spot_dict.keys():
                    all_spots += 1
                    (x1, y1, x2, y2) = spot
                    (x1, y1, x2, y2) = (int(x1), int(y1), int(x2), int(y2))
                    spot_img = image[y1:y2, x1:x2]
                    spot_img = cv2.resize(spot_img, (48,48))

                    label = self.make_prediction(spot_img, model, class_dictionary)
                    if label == 'empty':
                        cv2.rectangle(overlay, (int(x1), int(y1)), (int(x2), int(y2)), color, -1)
                        cnt_empty += 1
                cv2.addWeighted(overlay, alpha, new_image, 1 - alpha, 0, new_image)
                cv2.putText(new_image, "Available: %d spots" % cnt_empty, (30, 95), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
                cv2.putText(new_image, "Total: %d spots" % all_spots, (30, 125), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
                cv2.imshow('frame', new_image)
                if cv2.waitKey(10) & 0xFF == ord('q'):
                    break
        cv2.destroyAllWindows()
        cap.release()

(2)train.py


import numpy
import os
from keras import applications
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers
from keras.models import Sequential, Model
from keras.layers import Dropout, Flatten, Dense, GlobalAveragePooling2D
from keras import backend as k
from keras.callbacks import ModelCheckpoint, LearningRateScheduler, TensorBoard, EarlyStopping
from keras.models import Sequential
from keras.layers.normalization import BatchNormalization
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.initializers import TruncatedNormal
from keras.layers.core import Activation
from keras.layers.core import Flatten
from keras.layers.core import Dropout
from keras.layers.core import Dense

files_train = 0
files_validation = 0

cwd = os.getcwd()
folder = 'train_data/train'
for sub_folder in os.listdir(folder):
    path, dirs, files = next(os.walk(os.path.join(folder, sub_folder)))
    files_train += len(files)

folder = 'train_data/test'
for sub_folder in os.listdir(folder):
    path, dirs, files = next(os.walk(os.path.join(folder, sub_folder)))
    files_validation += len(files)

print(files_train, files_validation)

img_width, img_height = 48, 48
train_data_dir = "train_data/train"
validation_data_dir = "train_data/test"
nb_train_samples = files_train
nb_validation_samples = files_validation
batch_size = 32
epochs = 15
num_classes = 2

model = applications.VGG16(weights='imagenet', include_top=False, input_shape=(img_width, img_height, 3))

for layer in model.layers[:10]:
    layer.trainable = False
x = model.output
x = Flatten()(x)

predictions = Dense(num_classes, activation="softmax")(x)
model_final = Model(input=model.input, output=predictions)
model_final.compile(loss="categorical_crossentropy", optimizer = optimizers.SGD(lr=0.0001, momentum=0.9), metrics=["accuracy"])

train_datagen = ImageDataGenerator(rescale=1./255, horizontal_flip=True, fill_mode="nearest",
                                   zoom_range=0.1, width_shift_range=0.1, height_shift_range=0.1, rotation_range=5)
test_datagen = ImageDataGenerator(rescale=1./255, horizontal_flip=True, fill_mode="nearest",
                                  zoom_range=0.1, width_shift_range=0.1, height_shift_range=0.1, rotation_range=5)
train_generator = train_datagen.flow_from_directory(train_data_dir, target_size=(img_height, img_width), batch_size=batch_size, class_mode="categorical")
validation_generator = test_datagen.flow_from_directory(validation_data_dir, target_size=(img_height, img_width), class_mode="categorical")

checkpoint = ModelCheckpoint("car1.h5", monitor='val_acc', verbose=1, save_best_only=True, save_weights_only=False, mode='auto', period=1)
early = EarlyStopping(monitor='val_acc', min_delta=0, patience=10, verbose=1, mode='auto')
history_object = model_final.fit_generator(train_generator, samples_per_epoch=nb_train_samples, epochs=epochs,
                                           validation_data=validation_generator, nb_val_samples=nb_validation_samples, callbacks=[checkpoint, early])

(3)park_test.py


from __future__ import division
import matplotlib.pyplot as plt
import cv2
import os
import glob
import numpy as np

from keras.applications.imagenet_utils import preprocess_input
from keras.models import load_model
from keras.preprocessing import image
from PIL import Image

import pickle

from Parking import Parking
cwd = os.getcwd()

def img_process(test_images, park):
    white_yellow_images = list(map(park.select_rgb_white_yellow, test_images))
    park.show_images(white_yellow_images)

    gray_images = list(map(park.convert_gray_scale, white_yellow_images))
    park.show_images(gray_images)

    edge_images = list(map(lambda image: park.detect_edges(image), gray_images))
    park.show_images(edge_images)

    roi_images = list(map(park.select_region, edge_images))
    park.show_images(roi_images)

    list_of_lines = list(map(park.hough_lines, roi_images))

    line_images = []
    for image, lines in zip(test_images, list_of_lines):
        line_images.append(park.draw_lines(image, lines))
    park.show_images(line_images)

    rect_images = []
    rect_coords = []
    for image, lines in zip(test_images, list_of_lines):
        new_image, rects = park.identify_blocks(image, lines)
        rect_images.append(new_image)
        rect_coords.append(rects)
    park.show_images(rect_images)

    delineated = []
    spot_pos = []
    for image, rects in zip(test_images, rect_coords):
        new_image, spot_dict = park.draw_parking(image, rects)
        delineated.append(new_image)
        spot_pos.append(spot_dict)
    park.show_images(delineated)
    final_spot_dict = spot_pos[1]
    print(len(final_spot_dict))

    with open('spot_dict.pickle', 'wb') as handle:
        """#####################################################
        # Python中的pickle模块实现了基本的数据序列与反序列化。序列化对象可以在磁盘上保存对象,并在需要的时候读取出来。任何对象都可以执行序列化操作。
        #####################################################
        # (1)序列化-存档:pickle.dump(obj, file, protocol)
        # 输入参数           对象:就是你要存的东西,类型可以是list、string以及其他任何类型
        #                   文件:就是要将对象存储的目标文件
        #                   使用协议:有3种,索引0为ASCII(默认值),1是旧式2进制,2是新式2进制协议
        #       fw = open("pickleFileName.txt", "wb")
        #       pickle.dump("try", fw)
        #####################################################
        # (2)反序列化-读档:pickle.load(file)
        #       fr = open("pickleFileName.txt", "rb")
        #       result = pickle.load(fr)
        #####################################################"""
        pickle.dump(final_spot_dict, handle, protocol=pickle.HIGHEST_PROTOCOL)

    park.save_images_for_cnn(test_images[0], final_spot_dict)
    return final_spot_dict

def keras_model(weights_path):

    model = load_model(weights_path)
    return model

def img_test(test_images, final_spot_dict, model, class_dictionary):
    for i in range(len(test_images)):
        predicted_images = park.predict_on_image(test_images[i], final_spot_dict, model, class_dictionary)

def video_test(video_name, final_spot_dict, model, class_dictionary):
    name = video_name
    cap = cv2.VideoCapture(name)
    park.predict_on_video(name, final_spot_dict, model, class_dictionary, ret=True)

if __name__ == '__main__':

    test_images = [plt.imread(path) for path in glob.glob('test_images/*.jpg')]
    weights_path = 'car1.h5'
    video_name = 'parking_video.mp4'

    class_dictionary = {}
    class_dictionary[0] = 'empty'
    class_dictionary[1] = 'occupied'

    park = Parking()
    park.show_images(test_images)

    final_spot_dict = img_process(test_images, park)
    model = keras_model(weights_path)

    img_test(test_images, final_spot_dict, model, class_dictionary)
    video_test(video_name, final_spot_dict, model, class_dictionary)

(五)答题卡识别与判卷 —— cv2.putText()、cv2.countNonZero()

import cv2
import numpy as np
import matplotlib.pyplot as plt

def order_points(pts):

    rect = np.zeros((4, 2), dtype="float32")

    s = pts.sum(axis=1)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]

    diff = np.diff(pts, axis=1)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]
    return rect

def four_point_transform(image, pts):

    rect = order_points(pts)
    (tl, tr, br, bl) = rect

    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
    maxWidth = max(int(widthA), int(widthB))

    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int(heightA), int(heightB))

    dst = np.array([[0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]], dtype="float32")

    M = cv2.getPerspectiveTransform(rect, dst)
    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
    return warped

def sort_contours(cnts, method="left-to-right"):
    reverse = False
    i = 0
    if method == "right-to-left" or method == "bottom-to-top":
        reverse = True
    if method == "top-to-bottom" or method == "bottom-to-top":
        i = 1
    boundingBoxes = [cv2.boundingRect(c) for c in cnts]
    (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes), key=lambda b: b[1][i], reverse=reverse))
    return cnts, boundingBoxes

"""#############################################################
if __name__ == '__main__':
(1)"__name__"是Python的内置变量,用于指代当前模块。
(2)当哪个模块被直接执行时,该模块"__name__"的值就是"__main__"。
(3)当被导入另一模块时,"__name__"的值就是模块的真实名称。
#############################################################"""

ANSWER_KEY = {0: 1, 1: 4, 2: 0, 3: 3, 4: 1}

image = cv2.imread(r"images/test_01.png")
contours_img = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(blurred, 75, 200)
cnts, hierarchy = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(contours_img, cnts, -1, (0, 0, 255), 3)

docCnt = None
if len(cnts) > 0:
    cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
    for c in cnts:
        peri = cv2.arcLength(c, True)
        approx = cv2.approxPolyDP(c, 0.02*peri, True)
        if len(approx) == 4:
            docCnt = approx
            break

warped = four_point_transform(gray, docCnt.reshape(4, 2))
warped1 = warped.copy()
thresh = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]

thresh_Contours = thresh.copy()
cnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(thresh_Contours, cnts, -1, (0, 0, 255), 3)

questionCnts = []
for c in cnts:
    (x, y, w, h) = cv2.boundingRect(c)
    ar = w / float(h)
    if w >= 20 and h >= 20 and 0.9  ar  1.1:
        questionCnts.append(c)
questionCnts = sort_contours(questionCnts, method="top-to-bottom")[0]

correct = 0
for (q, i) in enumerate(np.arange(0, len(questionCnts), 5)):
    cnts = sort_contours(questionCnts[i:i + 5])[0]
    bubbled = None
    for (j, c) in enumerate(cnts):
        mask = np.zeros(thresh.shape, dtype="uint8")
        cv2.drawContours(mask, [c], -1, 255, -1)

        mask = cv2.bitwise_and(thresh, thresh, mask=mask)
        total = cv2.countNonZero(mask)
        if bubbled is None or total > bubbled[0]:
            bubbled = (total, j)
    color = (0, 0, 255)
    k = ANSWER_KEY[q]

    if k == bubbled[1]:
        color = (0, 255, 0)
        correct += 1
    cv2.drawContours(warped, [cnts[k]], -1, color, 3)

score = (correct / 5.0) * 100
print("[INFO] score: {:.2f}%".format(score))
"""###################################################################
在图像上添加文本内容: cv2.putText(img, str(i), (123,456), cv2.FONT_HERSHEY_PLAIN, 2, (0,255,0), 3)
各参数依次是:图片,添加的文字,左上角坐标,字体类型,字体大小,颜色,字体粗细
添加的字体:"{:.2f}%".format(score) ———— 表示添加score字符串。并且保留全部的整数位,小数点位保留两位。
###################################################################"""
cv2.putText(warped, "{:.1f}%".format(score), (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)

image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
contours_img = cv2.cvtColor(contours_img, cv2.COLOR_BGR2RGB)
plt.subplot(241),       plt.imshow(image, cmap='gray'),             plt.axis('off'),    plt.title('image')
plt.subplot(242),       plt.imshow(blurred, cmap='gray'),           plt.axis('off'),    plt.title('cv2.GaussianBlur')
plt.subplot(243),       plt.imshow(edged, cmap='gray'),             plt.axis('off'),    plt.title('cv2.Canny')
plt.subplot(244),       plt.imshow(contours_img, cmap='gray'),      plt.axis('off'),    plt.title('cv2.findContours')
plt.subplot(245),       plt.imshow(warped1, cmap='gray'),           plt.axis('off'),    plt.title('cv2.warpPerspective')
plt.subplot(246),       plt.imshow(thresh_Contours, cmap='gray'),   plt.axis('off'),    plt.title('cv2.findContours')
plt.subplot(247),       plt.imshow(warped, cmap='gray'),            plt.axis('off'),    plt.title('cv2.warpPerspective')
plt.show()

(六)背景建模(动态目标识别) —— cv2.getStructuringElement()、cv2.createBackgroundSubtractorMOG2()

"""########################################################
背景建模(检测动态目标)
方法一:帧差法
      介绍:由于场景中的目标在运动,目标的影像在不同图像帧中的位置不同。
           (1)该类算法对时间上连续的两帧图像进行差分运算,不同帧对应的像素点相减,判断灰度差的绝对值。
           (2)当绝对值超过一定阈值时,即可判断为运动目标,从而实现目标的检测功能。
      优缺点:帧差法非常简单,但是会引入噪音和空洞问题
方法二:混合高斯模型
      介绍:(1)背景训练,对图像中每个背景采用一个【混合高斯模型】进行模拟,每个背景的混合高斯的个数可以自适应。
           (2)测试阶段,对新来的像素进行GMM匹配,如果该像素值能够匹配其中一个高斯,则认为是背景,否则认为是前景。
      特点1:由于整个过程GMM模型在不断更新学习中,所以对动态背景有一定的鲁棒性。
      特点2:在视频中对于像素点的变化情况应当是符合高斯分布,背景的实际分布应当是多个高斯分布混合在一起,每个高斯模型也可以带有权重。
混合高斯模型学习方法
      1.首先初始化每个高斯模型矩阵参数。
      2.取视频中T帧图像数据用来训练高斯混合模型,并将第一个像素当做第一个高斯分布。
      3.其后的像素值与前一个高斯分布的均值进行比较,如果两者差值在3倍方差以内,则属于同一个高斯分布,并对其进行参数更新。否则用此像素创建一个新的高斯分布。
混合高斯模型测试方法
      在测试阶段,对新来像素点的值与混合高斯模型中的每一个均值进行比较,如果其差值在2倍的方差之间的话,则认为是背景,否则认为是前景(动态目标)。
      将前景赋值为255,背景赋值为0。这样就形成了一副前景二值图。
########################################################"""

import cv2
cap = cv2.VideoCapture('test.avi')
"""########################################################
构造卷积核:cv2.getStructuringElement(shape, ksize, anchor=None)
输入参数      shape:(1)Enumerator
                   (2)MORPH_RECT            矩形
                   (3)MORPH_CROSS       十字型
                   (4)MORPH_ELLIPSE     椭圆形
             ksize:  卷积核大小(元组类型)       例如:(3, 4)
             anchor: 元素内的描点位置,默认为 (-1, -1)表示形状中心
前提:背景是黑色,值为0,物体是白色,值为1
########################################################"""
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
fgbg = cv2.createBackgroundSubtractorMOG2()
while True:
    ret, frame = cap.read()

    fgmask = fgbg.apply(frame)
    fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
    contours, hierarchy = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    for c in contours:
        perimeter = cv2.arcLength(c, True)
        if perimeter > 188:
            x, y, w, h = cv2.boundingRect(c)
            cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
    cv2.imshow('frame', frame)
    cv2.imshow('fgmask', fgmask)
    k = cv2.waitKey(10) & 0xff
    if k == 27:
        break
cap.release()
cv2.destroyAllWindows()

(七)光流估计(轨迹点跟踪)—— cv2.goodFeaturesToTrack()、cv2.calcOpticalFlowPyrLK()

"""##########################################################################
光流是空间运动物体在观测成像平面上的像素运动的"瞬时速度",是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的相应关系,从而实现目标跟踪。
三要素(必要条件)      (1)亮度恒定:同一点随着时间的变化(在连续帧之间),其亮度(像素强度)不会发生改变。
                     (2)小运动:相邻像素具有相似的运动。
                          因为只有小运动情况下,才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数。
                     (3)空间一致:一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。
                          因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量。所以需要连立n多个方程求解。
- cv2.goodFeaturesToTrack() 确定要追踪的特征点
- cv2.calcOpticalFlowPyrLK() 追踪视频中的特征点
##########################################################################"""

import numpy as np
import cv2
cap = cv2.VideoCapture('test.avi')
feature_params = dict(maxCorners=150, qualityLevel=0.3, minDistance=12)
lk_params = dict(winSize=(15, 15), maxLevel=2)
color = np.random.randint(0, 255, (100, 3))

count = 0
while True:
    ret, old_frame = cap.read()
    count = count + 1
    if count == 235:
        break
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
"""#################################################################
确定要追踪的特征点:cv2.goodFeaturesToTrack( image, maxCorners, qualityLevel, minDistance, mask=noArray(),
                                          blockSize=3, bool useHarrisDetector=false, double k=0.04 );
输入参数            image:             输入图像,是八位的或者32位浮点型,单通道图像,所以有时候用灰度图
                  maxCorners:         返回最大的角点数,是最有可能的角点数,如果这个参数不大于0,那么表示没有角点数的限制。
                  qualityLevel:       图像角点的最小可接受参数,质量测量值乘以这个参数就是最小特征值,小于这个数的会被抛弃。
                  minDistance:        返回的角点之间最小的欧式距离。
                  mask:               检测区域。如果图像不是空的(它需要具有CV_8UC1类型和与图像相同的大小),它指定检测角的区域。
                  blockSize:          用于计算每个像素邻域上的导数协变矩阵的平均块的大小。
                  useHarrisDetector:  选择是否采用Harris角点检测,默认是false.

                  k:                  Harris检测的自由参数。
输出参数           corners:             输出为角点。
备注:角点最大数量(数量越多,效率慢),品质因子(品质因子越大,角点越少,但越大越好)、角点距离(在角点距离范围内,取N个角点中最好的一个角点)
#################################################################"""
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)
mask = np.zeros_like(old_frame)
while True:
    ret, frame = cap.read()
    if not ret:
        break
    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    """#################################################################
    # 追踪视频中的特征点:p1, status, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, winSize=(15, 15), maxLevel=2)
    # 输入参数        old_gray         前一帧图像
    #               frame_gray       当前帧图像
    #               p0               待跟踪的特征点向量
    #               nextPts          None
    #               winSize          搜索窗口的大小
    #               maxLevel         最大的金字塔层数
    # 输出参数    p1            跟踪特征点向量
    #           status        特征点是否找到,找到的状态为1,未找到的状态为0
    #################################################################"""
    p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)

    good_new = p1[st == 1]
    good_old = p0[st == 1]

    for i, (new, old) in enumerate(zip(good_new, good_old)):
        a, b = new.ravel()
        c, d = old.ravel()
        a = int(a); b = int(b); c = int(c); d = int(d)
        mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2)
        """#####################################################
        # 画直线段:cv2.line(img, pt1, pt2, color, thickness)
        # 输入参数        img               要划的线所在的图像;
        #                pt1               直线起点
        #                pt2               直线终点
        #                color             直线的颜色
        #                thickness=1       线条粗细
        #####################################################"""
        frame = cv2.circle(frame, (a, b), 5, color[i].tolist(), -1)

        """#####################################################
        # 绘制圆型:cv2.circle(图像, 圆心, 半径, 颜色, 厚度)
        # 输入参数         (1)图像:在该图像上绘图。
        #                (2)圆心:圆的中心坐标。坐标表示为两个值的元组, 即(X坐标值, Y坐标值)。
        #                (3)半径:圆的半径。
        #                (4)颜色:圆的边界线颜色。对于BGR, 我们传递一个元组。例如:(255, 0, 0)为蓝色。
        #                (5)厚度:正数表示线的粗细。其中:-1表示实心圆。
        #####################################################"""
    img = cv2.add(frame, mask)

    cv2.imshow('frame', img)
    k = cv2.waitKey(50) & 0xff
    if k == 27:
        break
    old_gray = frame_gray.copy()
    p0 = good_new.reshape(-1, 1, 2)

cv2.destroyAllWindows()
cap.release()

(八)DNN模块的分类 —— cv2.dnn.blobFromImage()

import utils_paths
import numpy as np
import cv2

rows = open("synset_words.txt").read().strip().split("\n")

classes = [r[r.find(" ") + 1:].split(",")[0] for r in rows]

net = cv2.dnn.readNetFromCaffe("bvlc_googlenet.prototxt", "bvlc_googlenet.caffemodel")

imagePaths = sorted(list(utils_paths.list_images("images/")))

image = cv2.imread(imagePaths[0])
resized = cv2.resize(image, (224, 224))
blob = cv2.dnn.blobFromImage(resized, 1, (224, 224), (104, 117, 123))
print("First Blob: {}".format(blob.shape))

net.setInput(blob)
preds = net.forward()

idx = np.argsort(preds[0])[::-1][0]

text = "Label: {}, {:.2f}%".format(classes[idx], preds[0][idx] * 100)

cv2.putText(image, text, (5, 25),  cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

cv2.imshow("Image", image)
cv2.waitKey(0)

images = []

for p in imagePaths[1:]:
    image = cv2.imread(p)
    image = cv2.resize(image, (224, 224))
    images.append(image)
blob = cv2.dnn.blobFromImages(images, 1, (224, 224), (104, 117, 123))
print("Second Blob: {}".format(blob.shape))

net.setInput(blob)
preds = net.forward()

for (i, p) in enumerate(imagePaths[1:]):
    image = cv2.imread(p)
    idx = np.argsort(preds[i])[::-1][0]
    text = "Label: {}, {:.2f}%".format(classes[idx], preds[i][idx] * 100)
    cv2.putText(image, text, (5, 25),  cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
    cv2.imshow("Image", image)
    cv2.waitKey(0)
"""##################################################################
blob = cv2.dnn.blobFromImage() ———— 改变图像的大小,然后令R,G,B三个通道分别减去均值(去除光照影响)。
输出参数:       Blob            例如:(1,3,224,224) 分别表示:图片数量、图片通道数、图像的宽、图像的高
输入参数        resized         要改变的图像
                缩放系数            我们当前用的时1,所以不变
                (224,224)       图像大小
                (104,117,123)   图像三通道均值。这三个均值是Imagenet提供的。
预测一个结果:cv2.dnn.blobFromImage()      处理单张图像
预测多个结果:cv2.dnn.blobFromImages()     处理多张图像
##################################################################"""

(九)矩形涂鸦画板 —— cv.namedWindow()、cv.setMouseCallback()

"""#########################################################################
编写一个矩形涂鸦画板
功能:鼠标左键按下拖动绘制矩形,鼠标左键弹起时完成绘制
      (1)按' c '键清空画板
      (2)按' ESC '键退出
#########################################################################"""
import numpy as np
import cv2
from random import randint

class Painter:
    def __init__(self) -> None:
        self.mouse_is_pressed = False
        self.last_pos = (-1, -1)
        self.width = 300
        self.height = 512
        self.img = np.zeros((self.width, self.height, 3), np.uint8)
        self.window_name = 'painter'
        self.color = None

    def run(self):
        print('画板,拖动鼠标绘制矩形框,按ESC退出,按c键清空画板')
        cv2.namedWindow(self.window_name)
        cv2.setMouseCallback(self.window_name, lambda event, x, y, flags, param: self.on_draw(event, x, y, flags, param))
        while True:
            cv2.imshow(self.window_name, self.img)
            k = cv2.waitKey(1) & 0xFF
            if k == ord('c'):
                self.clean()
            elif k == 27:
                break
        cv2.destroyAllWindows()

    def on_draw(self, event, x, y, flags, param):

        pos = (x, y)
        if event == cv2.EVENT_LBUTTONDOWN:
            self.mouse_is_pressed = True
            self.last_pos = pos
        elif event == cv2.EVENT_MOUSEMOVE:
            if self.mouse_is_pressed == True:
                self.begin_draw_rectangle(self.last_pos, pos)
        elif event == cv2.EVENT_LBUTTONUP:
            self.end_draw_rectangle(self.last_pos, pos)
            self.mouse_is_pressed = False

    def clean(self):
        cv2.rectangle(self.img, (0, 0), (self.height, self.width), (0, 0, 0), -1)

    def begin_draw_rectangle(self, pos1, pos2):
        if self.color is None:
            self.color = (randint(0, 256), randint(0, 256), randint(0, 256))
        cv2.rectangle(self.img, pos1, pos2, self.color, -1)

    def end_draw_rectangle(self, pos1, pos2):
        self.color = None

if __name__ == '__main__':
    p = Painter()
    p.run()

"""#########################################################################
11、创建鼠标回调函数:cv2.setMouseCallback(windowName, MouseCallback, param=None)
输入参数       windowName:         窗口名称
              MouseCallback:      鼠标响应回调函数
              param:              响应函数传递的的参数
#########################################################################
22、MouseCallback(int event, int x, int y, int flags,  * userdata)
输入参数       x:          鼠标的x坐标
              y:          鼠标的y坐标
              userdata:   可选参数
              event:      一个MouseEventTypes常量
                                  (1)cv.EVENT_FLAG_LBUTTON= 1,       左键拖拽
                                  (2)cv.EVENT_FLAG_RBUTTON= 2,       右键拖拽
                                  (3)cv.EVENT_FLAG_MBUTTON= 4,       中键不放
                                  (4)cv.EVENT_FLAG_CTRLKEY= 8,       按住ctrl不放
                                  (5)cv.EVENT_FLAG_SHIFTKEY= 16,     按住shift不放
                                  (6)cv.EVENT_FLAG_ALTKEY= 32,       按住alt不放
              flags:      一个MouseEventFlags常量
                                   (1)cv.EVENT_MOUSEMOVE= 0,       鼠标移动
                                   (2)cv.EVENT_LBUTTONDOWN= 1,     左键按下
                                   (3)cv.EVENT_RBUTTONDOWN= 2,     右键按下
                                   (4)cv.EVENT_MBUTTONDOWN= 3,     中键按下
                                   (5)cv.EVENT_LBUTTONUP= 4,       左键释放
                                   (6)cv.EVENT_RBUTTONUP= 5,       右键释放
                                   (7)cv.EVENT_MBUTTONUP= 6,       中键释放
                                   (8)cv.EVENT_LBUTTONDBLCLK= 7,   左键双击
                                   (9)cv.EVENT_RBUTTONDBLCLK= 8,   右键双击
                                   (10)cv.EVENT_MBUTTONDBLCLK= 9,   中健双击
                                   (11)cv.EVENT_MOUSEWHEEL= 10,     滚轮滑动
                                   (12)cv.EVENT_MOUSEHWHEEL= 11     横向滚轮滑动
#########################################################################"""

(十)创建一个用于改变颜色的轨迹条 —— createTrackbar()、cv2.getTrackbarPos()

共有三个功能:(1)创建轨迹条(2)打开控制开关,根据RGB设置背景色(3)打开控制开关,根据RGB设置画笔颜色,在画板上画画。

Opencv图像处理(全)
"""#########################################################################
(1)滑动条控制R、G、B的值
(2)开关按钮switch,用于确认是否使用自定义RGB改变原图。
        0:不改变原图     1:调色        2:调色画板
#########################################################################"""
import cv2
import numpy as np

def nothing(x):
    pass

def Mouseback(event, x, y, flags, param):
    if flags == cv2.EVENT_FLAG_LBUTTON and event == cv2.EVENT_MOUSEMOVE:
        cv2.circle(img, (x, y), 1, [b, g, r], 1)

img = np.zeros((300, 512, 3), np.uint8)
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.createTrackbar('R', 'image', 0, 255, nothing)
cv2.createTrackbar('G', 'image', 0, 255, nothing)
cv2.createTrackbar('B', 'image', 0, 255, nothing)
switch = 'OFF ON'
cv2.createTrackbar(switch, 'image', 0, 2, nothing)
while (1):
    cv2.imshow('image', img)
    k = cv2.waitKey(1)
    if k == ord('q'):
        break

    r = cv2.getTrackbarPos('R', 'image')
    g = cv2.getTrackbarPos('G', 'image')
    b = cv2.getTrackbarPos('B', 'image')
    s = cv2.getTrackbarPos(switch, 'image')
    if s == 0:
        img[:] = 0
    elif s == 1:
        img[:] = [b, g, r]
    elif s == 2:
        cv2.setMouseCallback('image', Mouseback)
cv2.destroyAllWindows()

"""#########################################################################
11、创建一个滑动条: cv2.createTrackbar(Track_name, img, min, max, TrackbarCallback)
输入参数:
                Track_name:     滑动条的名字。
                img:                滑动条所在画布。
                min:                滑动条的最小值。
                max:                滑动条的最大值。
                TrackbarCallback:   滑动条的回调函数。

22、获取滑动条的值: value = cv2.getTrackbarPos(Track_name, img)
输入参数:
                Track_name:     滑动条的名字。
                img:                滑动条所在画布。
输出参数:       滑动条当前所在的位置(值)。
#########################################################################"""

(十一)基于二值化实现人像抠图与背景替换 —— np.where()、np.uint8()


import cv2
import numpy as np
import matplotlib.pyplot as plt

q_img = cv2.imread('black.png')
b_img = cv2.imread('starry_night.jpg')

q_img = cv2.resize(q_img, (b_img.shape[1], b_img.shape[0]))
print(q_img.shape)
print(b_img.shape)

img = q_img.copy()
for i in range(img.shape[0]):
    for j in range(img.shape[1]):

        if 255 == img[i][j][0] and 255 == img[i][j][1] and 255 == img[i][j][2]:
            img[i][j] = 255
        else:
            img[i][j] = 0

img_t = np.where(img == 0, 1, 0)
img3 = np.uint8(q_img * img_t)

img_t = np.where(img_t == 1, 2, img_t)
img_t = np.where(img_t == 0, 1, img_t)
img_t = np.where(img_t == 2, 0, img_t)
img4 = np.uint8(b_img * img_t)

img5 = img4 + img3

titles = ['q_img', 'b_img', 'img', 'img3', 'img4', 'img5']
images = [q_img, b_img, img, img3, img4, img5]
for ii in range(6):
    images[ii] = cv2.cvtColor(images[ii], cv2.COLOR_BGR2RGB)
    plt.subplot(2, 3, ii+1)
    plt.imshow(images[ii], 'gray')
    plt.axis('off')
    plt.xticks([]), plt.yticks([])
plt.show()

(一)图像的读取、保存和显示 —— cv2.imread(),cv2.imwrite(),cv2.imshow()

(1)多张图(在同一个窗口)同时显示 plt.subplot()
(2)cv2.imshow()和plt.imshow()的区别

Opencv图像处理(全)
"""#####################################################################
cv2是opencv在python中的缩写;
Matplotlib 是一个 Python 库,可以通过 python 脚本创建二维图形和图表。
Matplotlib 中的 pyplot 模块,可以控制线条样式,字体属性,格式化轴等功能。且支持各种各样的图形绘制,如直方图,条形图,功率谱,误差图等。
#####################################################################"""
import cv2
import matplotlib.pyplot as plt

"""#####################################################################
11、读取图像:cv2.imread(img_path, flag)
输入参数
#           img_path:   图像的路径(若路径错误,则返回None。但不会报错)
            flag:       cv2.IMREAD_COLOR(也可以传入1)            (默认)加载彩色图像RGB
                    cv2.IMREAD_GRAYSCALE(也可以传入0)        将图像转换为灰度图,
                    cv2.IMREAD_UNCHANGED(也可以传入-1)   加载原图

备注1:OpenCV 支持JPG、PNG、TIFF等常见格式图像文件加载(默认读取的格式是BGR)(图像的格式是RGB)
备注2:转义字符\可以转义很多字符,比如:'\n'表示换行,'\t'表示制表符,'\\'表示\。当然如果不需要转义,可以使用(r'cat.hpg');

###################################
路径说明(路径中不能出现中文,否则系统异常提示。)
        cat_path = r'C:\Users\my\Desktop\py_test\cat.jpg'       # 绝对路径
        cat_path = r'cat.jpg'                                   # 相对路径(即当前同级目录):不指定路径,则默认当前.py文件的路径下。
        cat_path = r'./cat.jpg'                                 # 相对路径(即当前同级目录):与上同效。
        cat_path = r'../py_test/cat.jpg'                        # 相对路径(上级目录):py_test表示存放当前.py文件的文件夹
#####################################################################"""
cat_path = r'C:\Users\my\Desktop\py_test\cat.jpg'
img0 = cv2.imread(cat_path)
img1 = cv2.imread(cat_path, cv2.IMREAD_COLOR)
img2 = cv2.imread(cat_path, cv2.IMREAD_GRAYSCALE)
img3 = cv2.imread(cat_path, cv2.IMREAD_UNCHANGED)

"""#####################################################################
22、保存图像:cv2.imwrite(img_path_name, img)
输入参数        img_path_name:      自定义待保存图像的路径+名字
              img:                待保存图像
#####################################################################"""
cv2.imwrite('gray_cat.png', img2)

"""#####################################################################
33、显示图像:cv2.imshow(window_title, img)
输入参数        window_title:       自定义窗口的名字
                img:                待显示图像
备注1:窗口会自适应图像大小
备注2:指定多个窗口名称,可以显示多幅图像
备注3:显示多幅图像的时候,若cv2.imshow()指定相同的窗口名,这样后面显示的图像会覆盖前面的图像,从而只产生一个(连续)窗口。如:视频
#####################################################################"""
cv2.imshow('raw_img', img0)
cv2.imshow('cv2.IMREAD_COLOR', img1)
cv2.imshow('cv2.IMREAD_GRAYSCALE', img2)
cv2.imshow('cv2.IMREAD_UNCHANGED', img3)
cv2.waitKey(1000)
cv2.destroyAllWindows()
"""#####################################################################
键盘绑定函数:cv2.waitKey()
        (1)cv2.waitKey(0):       表示无限期的等待键盘输入,按任意键继续。如:空格键
        (2)cv2.waitKey(delay):   当 delay>0 (单位:ms)时使用,表示等待一定时间。1秒(s) = 1000毫秒
#####################################################################"""

plt.subplot(141),   plt.imshow(img0, 'gray'),     plt.title('raw_img')
plt.subplot(142),   plt.imshow(img1, 'gray'),     plt.title('cv2.IMREAD_COLOR')
plt.subplot(143),   plt.imshow(img2, 'gray'),     plt.title('cv2.IMREAD_GRAYSCALE')
plt.subplot(144),   plt.imshow(img3, 'gray'),     plt.title('cv2.IMREAD_UNCHANGED')
plt.show()

plt.imshow(img0)
plt.colorbar()
plt.show()

(1.1)图窗设置:cv2.namedWindow()、cv2.resizeWindow()、cv2.moveWindow()、cv2.setWindowProperty()。

import cv2
window_name = 'projector'
cv2.namedWindow(window_name, cv2.WINDOW_KEEPRATIO)
cv2.resizeWindow(window_name, 10, 20)
cv2.moveWindow(window_name, 100, 200)
cv2.setWindowProperty(window_name, cv2.WND_PROP_TOPMOST, 1)

im = cv2.imread('test01.png')
cv2.imshow(window_name, im)
cv2.waitKey(0)
cv2.destroyAllWindows()

参数cv2.namedWindow(winname, flags)创建命名窗口1winname窗口名称,用作窗口的标识符。2flags窗口属性设置标志。flags=cv2.WINDOW_NORMAL用户可以手动改变窗口大小flags=cv2.WINDOW_AUTOSIZE窗口大小自动适应图片大小,并且不可手动更改。flags=cv2.WINDOW_FREERATIO自适应比例flags=cv2.WINDOW_KEEPRATIO保持比例flags=cv2.WINDOW_OPENGL窗口创建的时候会支持OpenGLflags=cv2.WINDOW_GUI_EXPANEDE创建的窗口允许添加工具栏和状态栏。flags=cv2.WINDOW_GUI_NORMAL创建没有状态栏和工具栏窗口。flags=cv2.WINDOW_AUTOSIZE窗口大小自动适应图片大小,并且不可手动更改。 参数cv2.resizeWindow(winname, width, height)改变窗口大小1winname窗口名2width窗口宽度3height窗口高度 参数cv2.moveWindow(winname, x, y)设置窗口位置1winname窗口名2x窗口x轴位置3y窗口y轴位置 参数cv2.setWindowProperty(winname, prop_id, prop_value)设置窗口属性1winname窗口名2prop_id要编辑的窗口属性。如cv2.WINDOW_NORMAL、cv2.WINDOW_KEEPRATIO、cv2.WINDOW_FULLSCREEN等。3prop_value窗口属性的新值。如cv2.WND_PROP_FULLSCREEN, cv2.WND_PROP_AUTOSIZE, cv2.WND_PROP_ASPECT_RATIO等。

(1.2)图窗关闭:cv2.waitKey()、cv2.destroyAllWindows()

参数cv2.waitKey()键盘绑定函数1cv2.waitKey(0)表示无限期的等待键盘输入,按任意键继续。如:空格键2cv2.waitKey(delay)当 delay>0 (单位:ms)时使用,表示等待一定时间。1秒(s) = 1000毫秒 参数cv2.destroyAllwindows()销毁窗口1cv2.destroyAllwindows()摧毁所有窗口2cv2.destroyWindow(winname)摧毁指定的窗口

import cv2

vc = cv2.VideoCapture(r'picture\test.mp4')

if vc.isOpened():
    open, frame = vc.read()
else:
    open = False

while open:
    ret, frame = vc.read()

    if frame is None:
        break
    if ret == True:
        gray = cv2.cvtColor(frame,  cv2.COLOR_BGR2GRAY)
        cv2.imshow('result', gray)

        if cv2.waitKey(10) & 0xFF == 27:
            break
vc.release()
cv2.destroyAllWindows()

"""###################################################################
11、cv2.VideoCapture可以捕获摄像头,用数字来控制不同的设备。
        0:表示调用电脑自带摄像头。
        1:表示调用外接USB摄像头。

22、vc.isOpened():    检查视频是否可以打开(返回值:True/False)

33、vc.read():        读取视频的每一帧(图像)(返回值:True/False,图像彩色图)

44、cv2.imshow('frame',frame)将每一帧(图像)显示在一个叫frame的窗口上。
#       为什么会产生视频的效果:通过while循环,将图像固定显示在'frame'图窗上,每一帧会覆盖上一帧,就产生了视频的效果。
###################################################################"""

(三)图像的三色图 —— cv2.split() + cv.merge()

(1)图像分割得到三色图(BGR)
(2)将分割的三色图还原为彩色图
(3)保留R通道 + 保留G通道 + 保留B通道

Opencv图像处理(全)
import cv2
import matplotlib.pyplot as plt

cat_address = r'C:\Users\my\Desktop\pythonProject\picture\cat.jpg'
img = cv2.imread(cat_address)
cat = img[0:50, 0:200]

"""#############################################
图像分割得到三色图: b, g, r = cv2.split(img)
        功能:将多通道的图像分离成若干个单通道的图像,分割后的单通道图像尺寸大小相同。
        注意:分割后任意单通道都属于灰度图,而不是对应的颜色通道图;
#############################################"""
b, g, r = cv2.split(img)

"""#############################################
将分割的三色图还原为彩色图: img = cv2.merge((b, g, r))
        功能:将多幅图像合并成一幅多通道图像,合并后的通道数是所有输入图像通道数的总和。
        注意:所有输入图像的通道数可以不相同,但是所有图像需要具有相同的尺寸和数据类型
#############################################"""
img = cv2.merge((b, g, r))

cur_img_R = img.copy()
cur_img_R[:, :, 0] = 0
cur_img_R[:, :, 1] = 0

cur_img_G = img.copy()
cur_img_G[:, :, 0] = 0
cur_img_G[:, :, 2] = 0

cur_img_B = img.copy()
cur_img_B[:, :, 1] = 0
cur_img_B[:, :, 2] = 0

plt.subplot(131), plt.imshow(cur_img_B), plt.title('Red')
plt.subplot(132), plt.imshow(cur_img_G), plt.title('Green')
plt.subplot(133), plt.imshow(cur_img_R), plt.title('Blue')
plt.show()

(四)图像的边缘填充 —— cv2.copyMakeBorder()

Opencv图像处理(全)
import cv2
import matplotlib.pyplot as plt

"""########################################################
cv2.copyMakeBorder():用于在像相框一样的图像周围创建边框。
cv2.copyMakeBorder(src, top, bottom, left, right, borderType, value)
输入参数:    src          原图像。
              top          顶部方向的边框宽度, 以像素数为单位。
              bottom       底部方向的边框宽度(以像素数为单位)。
              left         沿左方向的像素数量的边框宽度。
              right        沿右方向的像素数的边框宽度。
              borderType   描述要添加哪种边框。
                      (1)BORDER_REPLICATE   :复制法,即用复制最边缘像素。
                      (2)BORDER_REFLECT     :反射法,对感兴趣的图像中的像素在两边进行复制。例如:fedcba | abcdefgh | hgfedcb
                      (3)BORDER_REFLECT_101 :反射法,即用以最边缘像素为轴,对称。gfedcb | abcdefgh | gfedcba
                      (4)BORDER_WRAP        :外包装法。abcdefgh | abcdefgh | abcdefgh
                      (5)BORDER_CONSTANT    :常量法,即用常数值填充。
              value:      可选参数, 如果边界类型为cv2.BORDER_CONSTANT, 则描述边界的颜色。
########################################################"""
cat_address = r'C:\Users\my\Desktop\pythonProject\picture\cat.jpg'
img = cv2.imread(cat_address)

top_size, bottom_size, left_size, right_size = (50, 50, 50, 50)
replicate  = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE)
reflect    = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT_101)
wrap       = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP)
constant   = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_CONSTANT, value=0)

plt.subplot(231),   plt.imshow(img, 'gray'),          plt.title('ORIGINAL')
plt.subplot(232),   plt.imshow(replicate, 'gray'),    plt.title('BORDER_REPLICATE')
plt.subplot(233),   plt.imshow(reflect, 'gray'),      plt.title('BORDER_REFLECT')
plt.subplot(234),   plt.imshow(reflect101, 'gray'),   plt.title('BORDER_REFLECT_101')
plt.subplot(235),   plt.imshow(wrap, 'gray'),         plt.title('BORDER_WRAP')
plt.subplot(236),   plt.imshow(constant, 'gray'),     plt.title('BORDER_CONSTANT')
plt.show()

(五)图像融合 —— cv2.addWeighted()

(1)图像截取
(2)图像加/减常数
(3)图像加/减另一个图像
(4)图像相加 cv2.add()

Opencv图像处理(全)
import cv2
import matplotlib.pyplot as plt

img_cat = cv2.imread(r'picture\cat.jpg')
img_dog = cv2.imread(r'picture\dog.jpg')
print(img_cat.shape)
print(img_dog.shape)

cat_piece = img_cat[100:300, 0:200]

img_plus = img_cat - 50

img_cat = cv2.resize(img_cat, (400, 480))
img_dog = cv2.resize(img_dog, (400, 480))
print(img_cat.shape)
print(img_dog.shape)

res_add1 = img_cat + img_dog
res_add2 = cv2.add(img_cat, img_dog)

plt.subplot(231),       plt.imshow(img_cat, 'gray'),        plt.title('img_cat_resize')
plt.subplot(232),       plt.imshow(img_dog, 'gray'),        plt.title('img_dog_resize')
plt.subplot(233),       plt.imshow(cat_piece, 'gray'),      plt.title('cat_piece')
plt.subplot(234),       plt.imshow(img_plus, 'gray'),       plt.title('fig1 - 50')
plt.subplot(235),       plt.imshow(res_add1, 'gray'),       plt.title('fig1 + fig2')
plt.subplot(236),       plt.imshow(res_add2, 'gray'),       plt.title('cv2.add')
plt.show()

"""#####################################################################
图像融合:cv2.addWeighted(src1, alpha, src2, beta, gamma)
功能:将两张相同shape的图像按权重进行融合
输入参数         src1/src2        图像1与图像2
                 alpha/beta       图像1与图像2对应的权重(融合后的图像偏向于权重高的一边)
                 gamma            相当于(y=a*x+b)中的截距。用于调节亮度
权重融合公式:dst = src1 * alpha + src2 * beta + gamma
#####################################################################"""
img_cat = cv2.resize(img_cat, (500, 414))
img_dog = cv2.resize(img_dog, (500, 414))
res = cv2.addWeighted(img_cat, 0.35, img_dog, 0.65, 2)
plt.imshow(res)
plt.show()

(六)颜色空间转换 —— cv2.cvtColor()

Opencv图像处理(全)
import cv2
import matplotlib.pyplot as plt

"""#####################################################################
opencv中颜色空间转换函数:cv2.cvtColor()
opencv中有多种色彩空间,包括 RGB、HSI、HSL、HSV、HSB、YCrCb、CIE XYZ、CIE Lab8种。
在opencv中默认的颜色空间是BGR。
#####################################################################"""
img_BGR = cv2.imread(r'picture/cat.jpg')
plt.subplot(3, 3, 1),    plt.imshow(img_BGR),   plt.axis('off'),      plt.title('BGR')

img_RGB = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2RGB)
plt.subplot(3, 3, 2),    plt.imshow(img_RGB),   plt.axis('off'),      plt.title('RGB')

img_GRAY = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2GRAY)
plt.subplot(3, 3, 3),    plt.imshow(img_GRAY),  plt.axis('off'),      plt.title('GRAY')

img_HSV = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2HSV)
plt.subplot(3, 3, 4),    plt.imshow(img_HSV),   plt.axis('off'),      plt.title('HSV')

img_YcrCb = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2YCrCb)
plt.subplot(3, 3, 5),    plt.imshow(img_YcrCb), plt.axis('off'),      plt.title('YcrCb')

img_HLS = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2HLS)
plt.subplot(3, 3, 6),    plt.imshow(img_HLS),   plt.axis('off'),      plt.title('HLS')

img_XYZ = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2XYZ)
plt.subplot(3, 3, 7),    plt.imshow(img_XYZ),   plt.axis('off'),      plt.title('XYZ')

img_LAB = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2LAB)
plt.subplot(3, 3, 8),    plt.imshow(img_LAB),   plt.axis('off'),      plt.title('LAB')

img_YUV = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2YUV)
plt.subplot(3, 3, 9),    plt.imshow(img_YUV),   plt.axis('off'),      plt.title('YUV')
plt.show()

(七)阈值处理 —— cv2.threshold()

Opencv图像处理(全)
import cv2
import matplotlib.pyplot as plt
"""#############################################################
图像阈值 ret, dst = cv2.threshold(src, thresh, max_val, type)
输入参数        dst:    输出图
                src:    输入图,只能输入单通道图像,通常来说为灰度图
                thresh:     阈值
                max_val:    当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
                type:       二值化操作的类型,包含以下5种类型:
                    (1) cv2.THRESH_BINARY             超过阈值部分取max_val(最大值),否则取0
                  (2) cv2.THRESH_BINARY_INV         THRESH_BINARY的反转
                    (3) cv2.THRESH_TRUNC              大于阈值部分设为阈值,否则不变
                    (4) cv2.THRESH_TOZERO             大于阈值部分不改变,否则设为0
                    (5) cv2.THRESH_TOZERO_INV         THRESH_TOZERO的反转
#############################################################"""
img_BGR = cv2.imread(r'picture/cat.jpg')
ret, thresh1 = cv2.threshold(img_BGR, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img_BGR, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img_BGR, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img_BGR, 127, 255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img_BGR, 127, 255, cv2.THRESH_TOZERO_INV)

titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img_BGR, thresh1, thresh2, thresh3, thresh4, thresh5]

for ii in range(6):
    plt.subplot(2, 3, ii + 1), plt.imshow(images[ii], 'gray')
    plt.title(titles[ii])
    plt.xticks([]), plt.yticks([])
plt.show()

(八)均值/高斯/方框/中值滤波 —— cv2.blur() + cv2.boxFilter() + cv2.GaussianBlur() + cv2.medianBlur()

Opencv图像处理(全)
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread(r'picture/cat.jpg')
"""######################################
均值滤波:cv2.blur(img, ksize) ———— 取卷积核所有元素的均值
输入参数      ksize 表示卷积核大小。   例如:(3, 3)
作用:对于椒盐噪声的滤除效果比较好。
######################################"""
blur = cv2.blur(img, (3, 3))

"""######################################
方框滤波:cv2.boxFilter(img, -1, (3, 3), normalize=True)
输入参数      normalize=True    选择归一化       即取所有元素之和除以卷积核大小(与均值滤波等同)
            normalize=False   不选择归一化     【容易越界】且当"元素之和>255",则等于255;
######################################"""
box_T = cv2.boxFilter(img, -1, (3, 3), normalize=True)
box_F = cv2.boxFilter(img, -1, (3, 3), normalize=False)

"""######################################
高斯滤波:cv2.GaussianBlur()
特点:高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的
######################################"""
aussian = cv2.GaussianBlur(img, (5, 5), 1)

"""######################################
中值滤波:cv2.medianBlur() ———— 取卷积核所有元素(从小到大排序)的中间值
作用:中值滤波对消除椒盐噪声非常有效,能够克服线性滤波器带来的图像细节模糊等弊端,能够有效保护图像边缘信息;
######################################"""
median = cv2.medianBlur(img, 5)

plt.subplot(2, 3, 1),    plt.imshow(img),        plt.title('raw')
plt.subplot(2, 3, 2),    plt.imshow(blur),       plt.title('blur')
plt.subplot(2, 3, 3),    plt.imshow(box_T),      plt.title('box_T')
plt.subplot(2, 3, 4),    plt.imshow(box_F),      plt.title('box_F')
plt.subplot(2, 3, 5),    plt.imshow(aussian),    plt.title('aussian')
plt.subplot(2, 3, 6),    plt.imshow(median),     plt.title('median')
plt.show()
"""######################################
np.hstack(img1, img2, img3)     在水平方向上平铺
np.vstack(img1, img2, img3)     在竖直方向上堆叠
######################################"""
res = np.hstack((blur, aussian, median))
cv2.imshow('median - Gaussian - average', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

res = np.vstack((blur, aussian, median))
cv2.imshow('median - Gaussian - average', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

(九)腐蚀与膨胀 —— cv2.erode() 与 cv2.dilate() + np.zeros() 与 np.ones()

Opencv图像处理(全)
import cv2
import numpy as np
import matplotlib.pyplot as plt
"""###########################################################
腐蚀操作:cv2.erode(src, kernel, iteration)
膨胀操作:cv2.dilate(src, kernel, iteration)
两者参数说明相同: src表示输入的图片, kernel表示方框的大小, iteration表示迭代的次数
###########################################################"""
img = cv2.imread(r'picture/dige.png')
kernel = np.ones((3, 3), np.uint8)

dilate_1 = cv2.dilate(img, kernel, iterations=1)
dilate_2 = cv2.dilate(img, kernel, iterations=2)
dilate_3 = cv2.dilate(img, kernel, iterations=3)

erosion_1 = cv2.erode(img, kernel, iterations=1)
erosion_2 = cv2.erode(img, kernel, iterations=2)
erosion_3 = cv2.erode(img, kernel, iterations=3)

plt.subplot(2, 3, 1),    plt.imshow(dilate_1),       plt.title('erode-1')
plt.subplot(2, 3, 2),    plt.imshow(dilate_2),       plt.title('erode-2')
plt.subplot(2, 3, 3),    plt.imshow(dilate_3),       plt.title('erode-3')
plt.subplot(2, 3, 4),    plt.imshow(erosion_1),      plt.title('dilate-1')
plt.subplot(2, 3, 5),    plt.imshow(erosion_2),      plt.title('dilate-2')
plt.subplot(2, 3, 6),    plt.imshow(erosion_3),      plt.title('dilate-3')
plt.show()
"""###########################################################
np.zeros()与np.ones():分别创建全0与全1的数组 —— 需导入numpy模块
两者输入参数相同,创建数组的方式也相同。
下面以np.zeros()为例
np.zeros(shape, dtype=float, order='C')
输入参数        (1)shape:生成numpy数组
            (2)dtype:指定生成的数据类型数据类型,可选参数
            (3)order:表示在内存中是以行为主存储还是以列为主存储,可选参数,c代表行优先(默认);F代表列优先
    创建一维数组:                      np.zeros(5)
    创建多维数组:                      np.zeros((5,2))
    创建int类型的数组:                 np.zeros((5,2),dtype=int)
    创建x为int类型,y为float类型的数组:np.zeros((5,2),dtype=[('x','int'),('y','float')]
###########################################################"""

(十)形态学变化 —— cv2.morphologyEx()

主要内容:开运算 + 闭运算 + 梯度计算 + 顶帽 + 黑帽

Opencv图像处理(全)
import cv2
import numpy as np
import matplotlib.pyplot as plt
"""#######################################
morphology
n. (生物)形态学;(语言学中的)词法,形态学;结构,形态
#######################################
morph
n. 形素,语素;形态;图像变换
v. (使)图像变形;将(图像)进行合成处理;改变,变化,变形
#######################################
形态学变化函数:cv2.morphologyEx(src, op, kernel)
参数说明:src传入的图片,op进行变化的方式, kernel表示方框的大小
op变化的方式有五种:
        开运算(open):             cv2.MORPH_OPEN              先腐蚀,再膨胀。                  开运算可以用来消除小黑点。
        闭运算(close):            cv2.MORPH_CLOSE             先膨胀,再腐蚀。                  闭运算可以用来突出边缘特征。
        形态学梯度(morph-grad):   cv2.MORPH_GRADIENT          膨胀后图像(减去)腐蚀图像。      可以突出团块(blob)的边缘,保留物体的边缘轮廓。
        顶帽(top-hat):            cv2.MORPH_TOPHAT            原始输入(减去)开运算结果。      将突出比原轮廓亮的部分。
        黑帽(black-hat):          cv2.MORPH_BLACKHAT          闭运算结果(减去)原始输入        将突出比原轮廓暗的部分。
#######################################"""
img = cv2.imread(r'picture/dige.png')
kernel = np.ones((5, 5), np.uint8)

img_open = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
img_close = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
img_grad = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
img_top = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
img_black = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)

plt.subplot(2, 3, 1),    plt.imshow(img),            plt.title('RAW')
plt.subplot(2, 3, 2),    plt.imshow(img_open),       plt.title('MORPH_OPEN')
plt.subplot(2, 3, 3),    plt.imshow(img_close),      plt.title('MORPH_CLOSE')
plt.subplot(2, 3, 4),    plt.imshow(img_grad),       plt.title('MORPH_GRADIENT')
plt.subplot(2, 3, 5),    plt.imshow(img_top),        plt.title('MORPH_TOPHAT')
plt.subplot(2, 3, 6),    plt.imshow(img_black),      plt.title('MORPH_BLACKHAT')
plt.show()

(十一)边缘检测算子 —— cv2.sobel()、cv2.Scharr()、cv2.Laplacian()、cv2.Canny()

(1) 不同算子的差异:Sobel算子、Scharr算子、Laplacian算子
(2) Canny 不同阈值的区别

Opencv图像处理(全)
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread(r'picture\lena.jpg')

sobel_Gx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
sobel_Gx_Abs = cv2.convertScaleAbs(sobel_Gx)

sobel_Gy = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
sobel_Gy_Abs = cv2.convertScaleAbs(sobel_Gy)

sobel_Gx_Gy_Abs = cv2.addWeighted(sobel_Gx_Abs, 0.5, sobel_Gy_Abs, 0.5, 0)

plt.subplot(2, 3, 1),    plt.imshow(img),               plt.title('RAW')
plt.subplot(2, 3, 2),    plt.imshow(sobel_Gx),          plt.title('sobel_Gx')
plt.subplot(2, 3, 3),    plt.imshow(sobel_Gx_Abs),      plt.title('sobel_Gx_Abs')
plt.subplot(2, 3, 4),    plt.imshow(sobel_Gy),          plt.title('sobel_Gy')
plt.subplot(2, 3, 5),    plt.imshow(sobel_Gy_Abs),      plt.title('sobel_Gy_Abs')
plt.subplot(2, 3, 6),    plt.imshow(sobel_Gx_Gy_Abs),   plt.title('sobel_Gx_Gy_Abs')
plt.show()

"""########################################
Sobel算子: 是一种常用的边缘检测算子。对噪声具有平滑作用,提供较为精确的边缘方向信息,但是边缘定位精度不够高。
Sobel算子: 是离散微分算子(discrete differentiation operator),它结合了高斯平滑和微分求导,用来计算图像灰度的近似梯度,梯度越大越有可能是边缘。
        边缘就是像素对应的灰度值快速变化的地方。如:黑到白的边界
        图像是二维的。Sobel算子在x,y两个方向求导,故有不同的两个卷积核(Gx, Gy),且Gx的转置等于Gy。分别反映了每一点像素在水平方向和在垂直方向上的亮度变换情况.

########################################
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
输入参数    src        输入图像
          ddepth     图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度;
          dx和dy     表示的是求导的阶数,0表示这个方向上没有求导,一般为0、1、2。
          ksize      卷积核大小,一般为3、5。
同时对x和y进行求导,会导致部分信息丢失。(不建议)- 分别计算x和y,再求和(效果好)
########################################
(1)cv2.CV_16S的说明
      (1)Sobel函数求完导数后会有负值,还有会大于255的值。
      (2)而原图像是uint8,即8位无符号数。所以Sobel建立图像的位数不够,会有截断。
      (3)因此要使用16位有符号的数据类型,即cv2.CV_16S。
(2)cv2.convertScaleAbs(): 给图像的所有像素加一个绝对值
      通过该函数将其转回原来的uint8形式。否则将无法显示图像,而只是一副灰色的窗口。
############################################################################################"""

img = cv2.imread(r'picture\lena.jpg', cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)

scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0)

laplacian = cv2.Laplacian(img, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)

res = np.hstack((sobelxy, scharrxy, laplacian))
cv2.imshow('Sobel, Scharr, Laplacian', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

"""############################################################################################
边缘检测Canny算子
(1)使用高斯滤波器,以平滑图像,滤除噪声。
(2)计算图像中每个像素点的梯度强度和方向。
(3)应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。保留大值,去除小值。
(4)应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
(5)通过抑制孤立的弱边缘最终完成边缘检测。
############################################################################################"""
img = cv2.imread(r'picture\lena.jpg', cv2.IMREAD_GRAYSCALE)
v1 = cv2.Canny(img, 80, 150)
v2 = cv2.Canny(img, 50, 100)

res = np.hstack((v1, v2))
cv2.imshow('Canny', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

(十二)图像金字塔 —— cv2.pyrUp()、cv2.pyrDown()

Opencv图像处理(全)
import cv2
import matplotlib.pyplot as plt

img = cv2.imread(r'picture\AM.png')
img_up = cv2.pyrUp(img)
img_up2 = cv2.pyrUp(img_up)

img_down = cv2.pyrDown(img)
img_down2 = cv2.pyrDown(img_down)

img_up_down = cv2.pyrDown(img_up)
img_down_up = cv2.pyrUp(img_down)

img_laplacian = img - img_down_up
plt.subplot(2, 4, 1),    plt.imshow(img),               plt.title('RAW')
plt.subplot(2, 4, 2),    plt.imshow(img_up),            plt.title('img_up')
plt.subplot(2, 4, 3),    plt.imshow(img_up2),           plt.title('img_up2')
plt.subplot(2, 4, 4),    plt.imshow(img_down),          plt.title('img_down')
plt.subplot(2, 4, 5),    plt.imshow(img_down2),         plt.title('img_down2')
plt.subplot(2, 4, 6),    plt.imshow(img_up_down),       plt.title('img_up_down')
plt.subplot(2, 4, 7),    plt.imshow(img_down_up),       plt.title('img_down_up')
plt.subplot(2, 4, 8),    plt.imshow(img_laplacian),     plt.title('img_laplacian')
plt.show()

"""############################################################################################
高斯金字塔: cv2.pyrUp 与 cv2.pyrDown
  cv2.pyrDown:  向下采样(缩小一倍)。          (1)对图像进行高斯内核卷积;(2)将所有偶数行和列去除;
  cv2.pyrUp:    向上采样(放大一倍),分辨率降低。(1)将图像每隔"一行与一列",全部填充0;(2)使用先前同样的高斯内核(乘以4)与放大后的图像卷积,获得近似值;
形成过程大致:     (1)对原图像进行低通滤波和降采样得到一个粗尺度的近似图像,即分解得到的低通近似图像,
                (2)对近似图像经过插值(即上采样)和低通滤波
                (3)计算它和原图像的差值,得到分解的带通分量。
                ——— 第一步:源图像先缩小后再放大; 第二步:源图像减去第一步操作后得到新图像。
图像缩放: (1)图像金字塔(2)resize()函数; 后者效果更好,不会降低分辨率。
############################################################################################"""

(十三)图像轮廓检测 —— cv2.findContours()、cv2.drawContours()、cv2.arcLength()、cv2.approxPolyDP()、cv2.rectangle()

(1) 轮廓的多边形拟合曲线:cv2.approxPolyDP()
(2) 用矩形画出轮廓的边界:cv2.boundingRect()、cv2.rectangle()
(3) 用外接圆画出轮廓的边界:cv2.minEnclosingCircle()、cv2.circle()

Opencv图像处理(全)
import cv2
import matplotlib.pyplot as plt

img = cv2.imread(r'picture\contours2.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)

draw_img1 = img.copy()
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

res1 = cv2.drawContours(draw_img1, contours, -1, (0, 0, 255), 2)

draw_img2 = img.copy()
contours1 = contours[0]
epsilon = 0.21*cv2.arcLength(contours1, True)
approx = cv2.approxPolyDP(contours1, epsilon, True)

res2 = cv2.drawContours(draw_img2, [approx], -1, (0, 0, 255), 2)

draw_img3 = img.copy()
x, y, w, h = cv2.boundingRect(contours1)
img_rectangle = cv2.rectangle(draw_img3, (x, y), (x+w, y+h), (0, 255, 0), 2)

draw_img4 = img.copy()
(x, y), radius = cv2.minEnclosingCircle(contours1)
center = (int(x), int(y))
radius = int(radius)
img_circle = cv2.circle(draw_img4, center, radius, (0, 255, 0), 2)

plt.subplot(2, 3, 1),    plt.imshow(img),               plt.title('RAW')
plt.subplot(2, 3, 2),    plt.imshow(res1),              plt.title('findContours')
plt.subplot(2, 3, 3),    plt.imshow(res2),              plt.title('approxPolyDP')
plt.subplot(2, 3, 4),    plt.imshow(draw_img3),         plt.title('rectangle')
plt.subplot(2, 3, 5),    plt.imshow(draw_img4),         plt.title('circle')
plt.show()

"""######################################################################
(1)轮廓检测:contours, hierarchy = cv2.findContours(img, mode, method)
输入参数      mode: 轮廓检索模式
                  (1)RETR_EXTERNAL:  只检索最外面的轮廓;
                  (2)RETR_LIST:      检索所有的轮廓,但检测的轮廓不建立等级关系,将其保存到一条链表当中,
                  (3)RETR_CCOMP:     检索所有的轮廓,并建立两个等级的轮廓。顶层是各部分的外部边界,内层是的边界信息;
                  (4)RETR_TREE:      检索所有的轮廓,并建立一个等级树结构的轮廓;(最常用)
              method: 轮廓逼近方法
                  (1)CHAIN_APPROX_NONE:      存储所有的轮廓点,相邻的两个点的像素位置差不超过1。               例如:矩阵的四条边。(最常用)
                  (2)CHAIN_APPROX_SIMPLE:     压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标。   例如:矩形的4个轮廓点。
输出参数      contours:所有的轮廓
              hierarchy:每条轮廓对应的属性
备注0:轮廓就是将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。
备注1:函数输入图像是二值图,即黑白的(不是灰度图)。所以读取的图像要先转成灰度的,再转成二值图。
备注2:函数在opencv2只返回两个值:contours, hierarchy。
备注3:函数在opencv3会返回三个值:img, countours, hierarchy
######################################################################
(2)绘制轮廓:v2.drawContours(image, contours, contourIdx, color, thickness) ———— (在图像上)画出图像的轮廓
输入参数        image:              需要绘制轮廓的目标图像,注意会改变原图
                contours:           轮廓点,上述函数cv2.findContours()的第一个返回值
                contourIdx:         轮廓的索引,表示绘制第几个轮廓。-1表示绘制所有的轮廓
                color:              绘制轮廓的颜色(RGB)
                thickness:          (可选参数)轮廓线的宽度,-1表示填充
备注:图像需要先复制一份copy(), 否则(赋值操作的图像)与原图会随之一起改变。
######################################################################
(3)计算轮廓的长度:retval = cv2.arcLength(curve, closed)
输入参数:      curve              轮廓(曲线)。
                closed             若为true,表示轮廓是封闭的;若为false,则表示打开的。(布尔类型)
#
输出参数:      retval             轮廓的长度(周长)。
######################################################################
(4)找出轮廓的多边形拟合曲线:approxCurve = approxPolyDP(contourMat, epsilon, closed);
输入参数:     contourMat:        轮廓点矩阵(集合)
               epsilon:           (double类型)指定的精度, 即原始曲线与近似曲线之间的最大距离。
               closed:            (bool类型)若为true, 则说明近似曲线是闭合的; 反之, 若为false, 则断开。
#
输出参数:     approxCurve:       轮廓点矩阵(集合);当前点集是能最小包容指定点集的。画出来即是一个多边形;
######################################################################
(5)绘制矩形边框:cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
      (x, y):         矩形定点
      (x+w, y+h):     矩形的宽高
      (0,0,225):      矩形的边框颜色;
      2:              矩形边框宽度
######################################################################"""

(十四)模板匹配 —— cv2.matchTemplate()、cv2.minMaxLoc()

Opencv图像处理(全)

import cv2
import matplotlib.pyplot as plt

img = cv2.imread(r'picture/lena.jpg', 0)
template = cv2.imread(r'picture/face.jpg', 0)
h, w = template.shape[::1]

res1 = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
min_val1, max_val1, min_loc1, max_loc1 = cv2.minMaxLoc(res1)
top_left1 = min_loc1
bottom_right1 = (top_left1[0] + w, top_left1[1] + h)

res2 = cv2.matchTemplate(img, template, cv2.TM_CCORR)
min_val2, max_val2, min_loc2, max_loc2 = cv2.minMaxLoc(res2)
top_left2 = max_loc2
bottom_right2 = (top_left2[0] + w, top_left2[1] + h)

res3 = cv2.matchTemplate(img, template, cv2.TM_CCOEFF)
min_val3, max_val3, min_loc3, max_loc3 = cv2.minMaxLoc(res3)
top_left3 = max_loc3
bottom_right3 = (top_left3[0] + w, top_left3[1] + h)

res4 = cv2.matchTemplate(img, template, cv2.TM_SQDIFF_NORMED)
min_val4, max_val4, min_loc4, max_loc4 = cv2.minMaxLoc(res4)
top_left4 = min_loc4
bottom_right4 = (top_left4[0] + w, top_left4[1] + h)

res5 = cv2.matchTemplate(img, template, cv2.TM_CCORR_NORMED)
min_val5, max_val5, min_loc5, max_loc5 = cv2.minMaxLoc(res5)
top_left5 = max_loc5
bottom_right5 = (top_left5[0] + w, top_left5[1] + h)

res6 = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
min_val6, max_val6, min_loc6, max_loc6 = cv2.minMaxLoc(res6)
top_left6 = max_loc6
bottom_right6 = (top_left6[0] + w, top_left6[1] + h)

img1 = img.copy();       img2 = img.copy();           img3 = img.copy()
img4 = img.copy();       img5 = img.copy();           img6 = img.copy()

cv2.rectangle(img1, top_left1, bottom_right1, 255, 2)
cv2.rectangle(img2, top_left2, bottom_right2, 255, 2)
cv2.rectangle(img3, top_left3, bottom_right3, 255, 2)
cv2.rectangle(img4, top_left4, bottom_right4, 255, 2)
cv2.rectangle(img5, top_left5, bottom_right5, 255, 2)
cv2.rectangle(img6, top_left6, bottom_right6, 255, 2)

plt.subplot(231),       plt.imshow(img1, cmap='gray'),         plt.axis('off'),        plt.title('cv2.TM_SQDIFF')
plt.subplot(232),       plt.imshow(img2, cmap='gray'),         plt.axis('off'),        plt.title('cv2.TM_CCORR')
plt.subplot(233),       plt.imshow(img3, cmap='gray'),         plt.axis('off'),        plt.title('cv2.TM_CCOEFF')
plt.subplot(234),       plt.imshow(img4, cmap='gray'),         plt.axis('off'),        plt.title('cv2.TM_SQDIFF_NORMED')
plt.subplot(235),       plt.imshow(img5, cmap='gray'),         plt.axis('off'),        plt.title('cv2.TM_CCORR_NORMED')
plt.subplot(236),       plt.imshow(img6, cmap='gray'),         plt.axis('off'),        plt.title('cv2.TM_CCOEFF_NORMED')
plt.show()
"""##############################################
模板匹配:cv2.matchTemplate(image, template, method)
输入图像:检测对象的图像
模板图像:待检测的对象特征
模板匹配方法:
          (1)cv2.TM_SQDIFF:         计算平方差。           计算出来的值越接近0,越相关
          (2)cv2.TM_CCORR:          计算相关性。           计算出来的值越大,越相关
          (3)cv2.TM_CCOEFF:         计算相关系数。         计算出来的值越大,越相关
          (4)cv2.TM_SQDIFF_NORMED:  计算(归一化)平方差。   计算出来的值越接近0,越相关
          (5)cv2.TM_CCORR_NORMED:   计算(归一化)相关性。   计算出来的值越接近1,越相关
          (6)cv2.TM_CCOEFF_NORMED:  计算(归一化)相关系数。  计算出来的值越接近1,越相关
(最好选择有归一化操作,效果好)
##############################################
获取匹配结果函数:min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(ret)
其中: ret是cv2.matchTemplate函数返回的矩阵;
      min_val, max_val, min_loc, max_loc分别表示最小值,最大值,最小值与最大值在图像中的位置
如果模板方法是平方差或者归一化平方差,要用min_loc; 其余用max_loc
##############################################"""

(十五)直方图(均衡化) —— cv2.calcHist()、img.ravel()、cv2.bitwise_and()、cv2.equalizeHist()、cv2.createCLAHE()

Opencv图像处理(全)
Opencv图像处理(全)
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread(r'picture\cat.jpg', 0)
hist = cv2.calcHist([img], [0], None, [256], [0, 256])
plt.hist(img.ravel(), 256)
plt.show()

img1 = cv2.imread(r'picture\cat.jpg', 1)
color = ('blue', 'green', 'red')
for i, col in enumerate(color):
    hist1 = cv2.calcHist([img1], [i], None, [256], [0, 256])
    plt.plot(hist1, color=col)
plt.show()
"""##############################################
统计每个像素的众数(备注:输入参数需用方括号[]表示)
直方图: cv2.calcHist(images,channels,mask,histSize,ranges)
输入参数:     images:          原图像图像格式必须为 uint8 或 float32。
             channels:        (1)灰度图:[0]; (2)彩色图像:[0] [1] [2],分别对应着BGR。
             mask:            掩模图像。(1)统计整幅图像的直方图,则设置为 None。(2)统计图像某一区域的直方图,则制作一个掩模图像。
             histSize:        BIN的数目。可以理解为迭代范围。如:[1]表示0,1,2...256;如[10]表示0~10,11~20...;如[256]表示0~256。
             ranges:          统计像素值范围。通常为[0~256]。

##############################################"""

mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv2.bitwise_and(img, img, mask=mask)
hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])
plt.subplot(221), plt.imshow(img, 'gray'),                  plt.title('Raw')
plt.subplot(222), plt.imshow(mask, 'gray'),                 plt.title('mask')
plt.subplot(223), plt.imshow(masked_img, 'gray'),           plt.title('cv2.bitwise_and')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask), plt.title('hist_full + hist_mask')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask), plt.title('hist_full + hist_mask')
plt.xlim([0, 256])
plt.show()

img = cv2.imread(r'picture\cat.jpg', 0)
equ = cv2.equalizeHist(img)

clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
res_clahe = clahe.apply(img)
plt.subplot(131),   plt.imshow(img, 'gray'),        plt.title('Raw')
plt.subplot(132),   plt.imshow(equ, 'gray'),        plt.title('cv2.equalizeHist')
plt.subplot(133),   plt.imshow(res_clahe, 'gray'),  plt.title('cv2.createCLAHE')
plt.show()

(十六)傅里叶变换 + 低通/高通滤波 —— cv2.dft()、cv2.idft()、np.fft.fftshift()、np.fft.ifftshift()、cv2.magnitud()

傅里叶变换
以时间作为参照来观察动态世界的方法我们称其为时域分析。
世间万物都在随着时间不停的改变,并且永远不会静止下来。但在频域中,你会发现世界是静止的、永恒不变的。
傅里叶告诉我们:任何周期函数,都可以看作是不同振幅,不同相位正弦波的叠加。
举例:利用对不同琴键不同力度,不同时间点的敲击,可以组合出任何一首乐曲。
傅里叶分析可分为傅里叶级数(Fourier Serie)和傅里叶变换(Fourier Transformation)。
傅里叶变换的作用
(1)高频:变化剧烈的灰度分量,例如:边界/图像的轮廓
(2)低频:变化缓慢的灰度分量,例如:一片大海
滤波器
(1)低通滤波器:只保留低频,会使得图像模糊

Opencv图像处理(全)
"""##############################################
傅里叶变换:cv2.dft(np.float32, cv2.DFT_COMPLEX_OUTPUT)
输入参数:  (1)输入图像需要先转换成np.float32 格式。
          (2)转换标识 - cv2.DFT_COMPLEX_OUTPUT - 用来输出一个复数阵列
逆傅里叶变换:cv2.idft(dft_shift)
输入参数:  (1)傅里叶变换后并位置转换后的频谱图像。

在OpenCV中,我们通过cv2.dft()来实现傅里叶变换,使用cv2.idft()来实现逆傅里叶变换。
    注意1:变换后得到原始图像的频谱信息。其中:频率为0的部分(零分量)会在左上角,需要使用numpy.fft.fftshift()函数,将其移动到中间位置。
    注意2:变换后的频谱图像是双通道的(实部,虚部)。需要使用cv2.magnitude函数,将幅度映射到灰度空间[0,255]内,使其以灰度图像显示出来。

cv2.magnitude(x-实部,y-虚部)
输入参数:   (1)浮点型x坐标值(实部)
           (2)浮点型y坐标值(虚部)
备注:两个参数的必须具有相同的大小(size)
##############################################"""

img = cv2.imread(r'picture\lena.jpg', 0)
dft = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:, :, 0],dft_shift[:, :, 1]))

rows, cols = img.shape
crow, ccol = int(rows/2), int(cols/2)

mask_low = np.zeros((rows, cols, 2), np.uint8)
mask_low[crow-30:crow+30, ccol-30:ccol+30] = 1

fshift_low = dft_shift * mask_low
f_ishift_low = np.fft.ifftshift(fshift_low)
img_low = cv2.idft(f_ishift_low)
img_low = cv2.magnitude(img_low[:, :, 0], img_low[:, :, 1])

mask_high = np.ones((rows, cols, 2), np.uint8)
mask_high[crow-30:crow+30, ccol-30:ccol+30] = 0

fshift_high = dft_shift * mask_high
f_ishift_high = np.fft.ifftshift(fshift_high)
img_high = cv2.idft(f_ishift_high)
img_high = cv2.magnitude(img_high[:, :, 0], img_high[:, :, 1])

plt.subplot(141),  plt.imshow(img, cmap='gray'),                 plt.title('Input Image'),        plt.xticks([]),   plt.yticks([])
plt.subplot(142),  plt.imshow(magnitude_spectrum, cmap='gray'),  plt.title('Magnitude Spectrum'), plt.xticks([]),   plt.yticks([])
plt.subplot(143),  plt.imshow(img_low, cmap='gray'),   plt.title('Low pass filter'),   plt.xticks([]),   plt.yticks([])
plt.subplot(144),  plt.imshow(img_high, cmap='gray'),  plt.title('High pass filter'),  plt.xticks([]),   plt.yticks([])
plt.show()

(十七)Harris角点检测 —— cv2.cornerHarris()、np.float32()

Opencv图像处理(全)
import cv2
import numpy as np

img = cv2.imread(r'picture\Black_and_white_chess.jpg')
print('img.shape:', img.shape)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
"""#################################################
Harris角点检测:res = cv2.cornerHarris(img_gray, blockSize, k_size, k)
输入参数       img_gray       数据类型为 float32 的输入图像。
              blockSize      角点检测中要考虑的领域大小        (一般等于2)
              k_size         Sobel求导中使用的窗口大小        (一般等于3)
              k              方程中检测器的自由参数, 取值参数为 [0,04,0.06].

角点定义: 角点是一个无论框框往哪边移动 框框内像素值都会变化很大的情况而定下来的点。
##################################################
float16 半精度浮点数,包括:1 个符号位,5 个指数位,10 个尾数位
float32 单精度浮点数,包括:1 个符号位,8 个指数位,23 个尾数位
float64 双精度浮点数,包括:1 个符号位,11 个指数位,52 个尾数位
##################################################"""
Harris_dst = cv2.cornerHarris(gray, 2, 3, 0.04)
print('dst.shape:', Harris_dst.shape)

img[Harris_dst > 0.01 * Harris_dst.max()] = [0, 0, 255]

cv2.imshow('Harris_dst', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

(十八)SIFT尺度不变特征检测 —— cv2.xfeatures2d.SIFT_create()、sift.detectAndCompute()、sift.detect()、sift.compute()、cv2.drawKeypoints

Opencv图像处理(全)
"""#########################################################
SIFT 图像特征检测算法
(Scale-invariant feature transform,SIFT)尺度不变特征变换。
特点:具有尺度不变性。即对图片进行放缩、变形、模糊、明暗变化、光照变化、添加噪声,甚至是使用不同的相机拍摄不同角度的照片的情况下,SIFT都能检测到稳定的特征点,并建立对应关系。
缺点:计算量比较大,很难实时
对比:Harris角点检测算法最大的缺陷是不具有尺度不变性。当图片放大后,原来能检测到的角点就变成了边线,就检测不到了
#########################################################
在Opencv中,SIFT函数从3.4.3版本以上已经涉及到专利保护。顾Opencv需要降版本使用。
卸载旧版本:pip uninstall opencv-python
          pip uninstall opencv-contrib-python
安装新版本:pip install opencv-python==3.4.1.15
          pip install opencv-contrib-python==3.4.1.15
#########################################################"""
import cv2

img = cv2.imread('test_1.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

sift = cv2.xfeatures2d.SIFT_create()
"""#########################################################
检测关键点并计算描述子:key_points, des = sift.detectAndCompute(img, None)
输出参数:key_points为图像关键点;dst为sift特征向量,通常是128维。
#
其中,sift.detectAndCompute()可以拆分为以下两个函数:sift.detect()和sift.compute()。
      key_points = sift.detect(gray, None)         # 找出图像中的关键点
      kp, dst = sift.compute(key_points)           # 计算关键点对应的sift特征向量
#########################################################"""
key_points, des = sift.detectAndCompute(gray, None)

"""#########################################################
在图中画出关键点:ret = cv2.drawKeypoints(gray, key_points, img)
输入参数:gray表示输入图片;kp表示关键点;img表示输出的图片
#########################################################"""
img = cv2.drawKeypoints(gray, key_points, img)

cv2.imshow('key_points', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

(十九)暴力特征匹配 —— cv2.BFMatcher_create()、bf.match()、bf_knn.knnMatch()、cv2.drawMatches()

Opencv图像处理(全)
"""########################################################
暴力特征检测主要流程
              (1)先在查询描述符中取一个关键点的描述符,将其与训练描述符中的所有关键点描述符进行比较。
              (2)每次比较后会计算出一个距离值,距离最小的值对应最佳匹配结果。
              (3)所有描述符比较完后,匹配器返回匹配结果列表。
########################################################
在Opencv中,SIFT函数从3.4.3版本以上已经涉及到专利保护。顾Opencv需要降版本使用。
卸载旧版本:pip uninstall opencv-python
          pip uninstall opencv-contrib-python
安装新版本:pip install opencv-python==3.4.1.15
          pip install opencv-contrib-python==3.4.1.15
########################################################"""
import cv2
import matplotlib.pyplot as plt

img1 = cv2.imread(r'picture\box.png')
img2 = cv2.imread(r'picture\box_in_scene.png')

sift = cv2.xfeatures2d.SIFT_create()

kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)

bf = cv2.BFMatcher_create(cv2.NORM_L1, crossCheck=False)
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance)
result = cv2.drawMatches(img1, kp1, img2, kp2, matches[:15], None)

bf_knn = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=False)
ms_knn = bf_knn.knnMatch(des1, des2, k=2)

good = []
for m, n in ms_knn:
    if m.distance < 0.75 * n.distance:
        good.append(m)
img_DEFAUL = cv2.drawMatches(img1, kp1, img2, kp2, good[:20], None, flags=cv2.DrawMatchesFlags_DEFAUL)
img_NO_POINTS = cv2.drawMatches(img1, kp1, img2, kp2, good[:20], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
img_KEYPOINTS = cv2.drawMatches(img1, kp1, img2, kp2, good[:20], None, flags=cv2.DrawMatchesFlags_DRAW_RICH_KEYPOINTS)

plt.subplot(231),       plt.imshow(img1),               plt.title('raw_img1')
plt.subplot(232),       plt.imshow(img2),               plt.title('raw_img2')
plt.subplot(233),       plt.imshow(result),             plt.title('match_result')

plt.subplot(234),       plt.imshow(img_DEFAUL),         plt.title('knnMatch_DEFAUL')
plt.subplot(235),       plt.imshow(img_NO_POINTS),      plt.title('knnMatch_NO_POINTS')
plt.subplot(236),       plt.imshow(img_KEYPOINTS),      plt.title('knnMatch_KEYPOINTS')
plt.show()
"""########################################################
暴力匹配器:bf = cv2.BFMatcher_create(normType, crossCheck)
输出参数          bf               返回的暴力匹配器对象
输入参数          crossCheck       默认为False, 表示匹配器为每个查询描述符找到k个距离最近的匹配描述符。若为True, 则只返回满足交叉验证条件的匹配结果。
                 normType         距离测量类型
                    方法(1)【SIFT】描述符使用cv2.NORM_L1或cv2.NORM_L2,默认为cv2.NORM_L2。
                    方法(2)【ORB】描述符使用cv2.NORM_HAMMING
###########################
(1)匹配最佳结果:ms = bf.match(des1, des2)
输出参数          ms          为每个关键点的最佳匹配结果(距离值越小匹配度越高)
输入参数          des1        为查询描述符
                 des2         为训练描述符
###########################
(2)匹配指定数量的最佳结果:ms = knnMatch(des1, des2, k=n)
输出参数          ms          为返回的匹配结果, 每个列表元素是一个子列表, 它包含了由参数k指定个数的DMatch对象
输入参数          des1        为查询描述符
                 des2       为训练描述符
                 k          为返回的最佳匹配个数
########################################################
绘制最佳匹配:outImg = cv2.drawMatches(img1, keypoints1, img2, keypoints2, matches1to2[, matchColor[, singlePointColor[, matchesMask[, flags]]]])
输出参数         outImg为返回的绘制结果图像, 图像中查询图像与训练图像中匹配的关键点个两点之间的连线为彩色
输入参数        (1)img1为查询图像               (2)keypoints1为img1的关键点
               (3)img2为训练图像               (4)keypoints2为img2的关键点
               (5)matches1to2         img1与img2的匹配结果
               (6)matchColor          关键点和链接线的颜色, 默认使用随机颜色
               (7)singlePointColor    单个关键点的颜色, 默认使用随机颜色
               (8)matchesMask         掩膜, 用于决定绘制哪些匹配结果, 默认为空, 表示绘制所有匹配结果
flags为标志, 可设置为下列参数值:
      (1)cv2.DrawMatchesFlags_DEFAUL                     (默认方式)绘制两个源图像、匹配项和单个关键点, 没有围绕关键点的圆以及关键点的大小和方向
      (2)cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS     不会绘制单个关键点
      (3)cv2.DrawMatchesFlags_DRAW_RICH_KEYPOINTS        在关键点周围绘制具有关键点大小和方向的圆圈
########################################################"""

(二十)图像缩放+镜像+平移+旋转+仿射变换+透视变换 —— cv2.resize()、cv2.getRotationMatrix2D()、cv2.getAffineTransform()、cv2.getPerspectiveTransform()、cv2.warpPerspective()、cv2.warpAffine()

Opencv图像处理(全)
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread(r"picture/1.jpg", 1)
imgInfo = img.shape
height = imgInfo[0]; width = imgInfo[1]; deep = imgInfo[2]

dstHeight = int(height/2)
dstWidth = int(width/2)
dst_resize_dir = cv2.resize(img, (dstWidth, dstHeight))

dst_resize_linear = np.zeros([dstHeight, dstWidth, 3], np.uint8)
for i in range(dstHeight):
    for j in range(dstWidth):
        iNew = i * (height * 1.0 / dstHeight)
        jNew = j * (width * 1.0 / dstWidth)
        dst_resize_linear[i, j] = img[int(iNew), int(jNew)]

dst_mirror = np.zeros([height * 2, width, deep], np.uint8)
for i in range(height):
    for j in range(width):
        dst_mirror[i, j] = img[i, j]
        dst_mirror[height * 2 - i - 1, j] = img[i, j]
for i in range(width):
    dst_mirror[height, i] = (0, 0, 255)

dst_trans = np.zeros(imgInfo, np.uint8)
for i in range(height):
    for j in range(width - 200):
        dst_trans[i, j+100] = img[i, j]

matRotate = cv2.getRotationMatrix2D((height*0.5, width*0.5), 45, 0.7)
dst_rotate = cv2.warpAffine(img, matRotate, (height, width))

matSrc = np.float32([[0, 0], [0, height-1], [width-1, 0]])
matDst = np.float32([[50, 50], [100, height-50], [width-200, 100]])
matAffine = cv2.getAffineTransform(matSrc, matDst)
dst_affine = cv2.warpAffine(img, matAffine, (height, width))

matDst1 = np.float32([[50, 50], [100, height-50], [width-200, 100], [width-200, height-50]])
matSrc1 = np.float32([[0, 0], [0, height-1], [width-1, 0], [width-1, height-1]])
matwarp = cv2.getPerspectiveTransform(matDst1, matSrc1)
dst_Perspective = cv2.warpPerspective(img, matwarp, (height, width))
dst_Perspective = np.hstack((dst_affine, dst_Perspective))

plt.subplot(241),       plt.imshow(img, 'gray'),                    plt.title('img')
plt.subplot(242),       plt.imshow(dst_resize_dir, 'gray'),         plt.title('dst_resize_dir (coordinates)')
plt.subplot(243),       plt.imshow(dst_resize_linear, 'gray'),      plt.title('dst_resize_linear (coordinates)')
plt.subplot(244),       plt.imshow(dst_mirror, 'gray'),             plt.title('dst_mirror')
plt.subplot(245),       plt.imshow(dst_trans, 'gray'),              plt.title('dst_trans')
plt.subplot(246),       plt.imshow(dst_rotate, 'gray'),             plt.title('dst_rotate')
plt.subplot(247),       plt.imshow(dst_affine, 'gray'),             plt.title('dst_affine')
plt.subplot(248),       plt.imshow(dst_Perspective, 'gray'),        plt.title('dst_Perspective')
plt.show()

"""################################################
图像缩小或放大:cv2.resize(src, dsize, fx=0, fy=0, interpolation=INTER_LINEAR)
输入参数:       src                输入图片
               dsize            (1)矩阵参数缩放到指定大小(width,height);           例如:cv2.resize(img_dog, (500, 414))
                                (2)矩阵参数为(0,0),原图像缩放倍数通过fx, fy来控制;    例如:cv2.resize(img_dog, (0, 0), fx=4, fy=4)
               fx, fy             沿x轴,y轴的缩放系数
               interpolation      插值方法,有以下五种(可选参数)
                          (1)INTER_NEAREST       最近邻插值                   (2)INTER_LINEAR        双线性插值(默认设置)
                          (3)INTER_AREA          使用像素区域关系进行重采样。     (4)INTER_CUBIC         4x4像素邻域的双三次插值
                          (5)INTER_LANCZOS4      8x8像素邻域的Lanczos插值
################################################
获取旋转矩阵:rot_mat = cv2.getRotationMatrix2D(center, angle, scale)
输入参数          center        旋转的中心点。一般是图像的中心,取图像长宽的一半
                angle         旋转的角度。正值是顺时针旋转,负值是逆时针旋转
                scale         缩放比例。
################################################
获取仿射变换矩阵:matAffine = cv2.getAffineTransform(matSrc, matDst)
输入参数        matSrc          原图的三个点坐标
              matDst          仿射的三个点坐标
输出参数        matAffine       仿射变换矩阵
################################################
计算齐次变换矩阵:cv2.getPerspectiveTransform(rect, dst)
输入参数        rect输入图像的四个点(四个角)
                dst输出图像的四个点(方方正正的图像对应的四个角)
################################################
仿射变换:cv2.warpPerspective(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)
透视变换:cv2.warpAffine(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)
                src:输入图像     dst:输出图像
                M:2×3的变换矩阵
                dsize:变换后输出图像尺寸
                flag:插值方法
                borderMode:边界像素外扩方式
                borderValue:边界像素插值,默认用0填充
#
(Affine Transformation)可实现旋转,平移,缩放,变换后的平行线依旧平行。
(Perspective Transformation)即以不同视角的同一物体,在像素坐标系中的变换,可保持直线不变形,但是平行线可能不再平行。
#
备注:cv2.warpAffine需要与cv2.getRotationMatrix2D/cv2.getAffineTransform/cv2.getPerspectiveTransform搭配使用。
################################################"""

Original: https://blog.csdn.net/shinuone/article/details/126022763
Author: 胖墩会武术
Title: Opencv图像处理(全)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/668568/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球