当前位置: 首页 > news >正文

Opencv Python图像处理笔记二:图像变换、卷积、形态学变换

文章目录

  • 前言
  • 一、几何变换
    • 1.1 缩放
    • 1.2 平移
    • 1.3 旋转
    • 1.4 翻转
    • 1.5 仿射
    • 1.6 透视
  • 二、低通滤波
    • 2.1 均值滤波
    • 2.2 高斯滤波
    • 2.3 中值滤波
    • 2.4 双边滤波
    • 2.5 自定义滤波
  • 三、高通滤波
    • 3.1 Sobel
    • 3.2 Scharr
    • 3.3 Laplacian
    • 3.4 Canny
  • 四、图像金字塔
    • 4.1 高斯金字塔
    • 4.2 拉普拉斯金字塔
  • 五、形态学
    • 5.1 腐蚀
    • 5.2 膨胀
    • 5.3 运算
  • 六、直方图
    • 6.1 计算
    • 6.2 均衡
    • 6.3 反向投影
  • 七、轮廓
    • 7.1 查找显示
    • 7.2 常用特征
  • 参考

前言

图像处理是计算机视觉领域中的核心技术之一,它涉及到对图像进行各种变换、滤波、金字塔构建、形态学操作等一系列处理。在本篇博文中,我们将深入探讨使用OpenCV和Python进行图像处理的各种技术和方法。从几何变换到滤波、金字塔构建再到形态学操作,我们将逐步介绍并实践这些重要的图像处理技术,帮助读者更好地理解和应用于实际项目中。

一、几何变换

1.1 缩放

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltdef generateCanvas(img, img2):canvas = np.zeros((max(img.shape[0], img2.shape[0]),img.shape[1] + img2.shape[1], 3), dtype=np.uint8)# 将图像1和图像2放置在画布上canvas[:img.shape[0], :img.shape[1]] = imgcanvas[:img2.shape[0],img.shape[1]:] = img2return canvasdef main():img = cv.imread('sudoku.png')assert img is not None, "file could not be read, check with os.path.exists()"# dsize:输出图像的大小。如果这个参数不为None,那么就代表将原图像缩放到这个Size(width,height)指定的大小;#        如果这个参数为0,那么原图像缩放之后的大小就要通过下面的公式来计算:#               dsize = Size(round(fx*src.cols), round(fy*src.rows))#       其中,fx和fy就是下面要说的两个参数,是图像width方向和height方向的缩放比例。#  fx:width方向的缩放比例,如果它是0,那么它就会按照(double)dsize.width/src.cols来计算;#  fy:height方向的缩放比例,如果它是0,那么它就会按照(double)dsize.height/src.rows来计算;# interpolation:这个是指定插值的方式,图像缩放之后,肯定像素要进行重新计算的,就靠这个参数来指定重新计算像素的方式,有以下几种:#     INTER_NEAREST              - 最近邻插值#     INTER_LINEAR               - 双线性插值,如果最后一个参数你不指定,默认使用这种方法#     INTER_AREA                 - 使用像素区域关系进行重采样#     INTER_CUBIC                - 4x4像素邻域内 的双立方插值#     INTER_LANCZOS4             - 8x8像素邻域内的Lanczos插值fx = 0.6fy = 0.6img_half_nearest = cv.resize(img, dsize=None, fx=fx, fy=fy, interpolation=cv.INTER_NEAREST)img_half_linear = cv.resize(img, dsize=None, fx=fx, fy=fy, interpolation=cv.INTER_LINEAR)img_half_area = cv.resize(img, dsize=None, fx=fx,fy=fy, interpolation=cv.INTER_AREA)img_half_cubic = cv.resize(img, dsize=None, fx=fx, fy=fy, interpolation=cv.INTER_CUBIC)img_half_lanczos4 = cv.resize(img, dsize=None, fx=fx, fy=fy, interpolation=cv.INTER_LANCZOS4)titles = ['nearest', 'linear', 'area', 'cubic', 'Lanczos']images = [img_half_nearest, img_half_linear,img_half_area, img_half_cubic, img_half_lanczos4]# canvas = generateCanvas(img, img_half_linear)# cv.imshow('test', canvas)for i in range(len(images)):show = cv.cvtColor(generateCanvas(img, images[i]), cv.COLOR_BGR2RGB)plt.subplot(2, 3, i+1), plt.imshow(show)plt.title(titles[i])plt.xticks([]), plt.yticks([])plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

resize

1.2 平移

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltdef main():img = cv.imread('lena.png')assert img is not None, "file could not be read, check with os.path.exists()"rows, cols, _ = img.shapeM = np.float32([[1, 0, 100], [0, 1, 50]])# M: 2 * 3 矩阵# 根据下面的公式,想实现平移,可以通过构造M=[[1, 0, x], [0, 1, y]],实现向右平移x,向下平移y# dst(x, y) = (M[0,0] * x  + M[0, 1] * y + M[0, 2], M[1, 0] * x + M[1, 1] * y + M[1, 2])img_right_down = cv.warpAffine(img, M, (cols, rows))M = np.float32([[1, 0, -100], [0, 1, -50]])img_left_up = cv.warpAffine(img, M, (cols, rows))titles = ['origin', 'right down', 'left up']images = [img, img_right_down, img_left_up]for i in range(len(images)):show = cv.cvtColor(images[i], cv.COLOR_BGR2RGB)plt.subplot(1, 3, i+1), plt.imshow(show)plt.title(titles[i])plt.xticks([]), plt.yticks([])plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_16_img_translate.png

1.3 旋转

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltdef main():img = cv.imread('lena.png')assert img is not None, "file could not be read, check with os.path.exists()"rows, cols, _ = img.shapeM = cv.getRotationMatrix2D(center=(rows / 2, cols / 2), angle=-30, scale=1)img_rotete_minus_30 = cv.warpAffine(img, M, (cols, rows))M = cv.getRotationMatrix2D(center=(rows / 2, cols / 2), angle=30, scale=1)img_rotete_30 = cv.warpAffine(img, M, (cols, rows))titles = ['origin', 'rotate -30 degree', 'rotate 30 degree']images = [img, img_rotete_minus_30, img_rotete_30]for i in range(len(images)):show = cv.cvtColor(images[i], cv.COLOR_BGR2RGB)plt.subplot(1, 3, i+1), plt.imshow(show)plt.title(titles[i])plt.xticks([]), plt.yticks([])plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_17_img_rotate

1.4 翻转

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltdef main():img = cv.imread('lena.png')assert img is not None, "file could not be read, check with os.path.exists()"# 竖直翻转img_vertical = cv.flip(img, flipCode=1)# 水平翻转img_horizontal = cv.flip(img, flipCode=0)# 两者img_both = cv.flip(img, flipCode=-1)title = ['Origin', 'flipCode=1,Vertical','flipCode=0,Horizontal', 'flipCode=-1,Both']# 对应的图像imgs = [img, img_vertical, img_horizontal, img_both]for i in range(len(imgs)):plt.subplot(2, 2, i + 1)plt.imshow(cv.cvtColor(imgs[i], cv.COLOR_BGR2RGB))plt.title(title[i])plt.axis('off')plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_18_img_flip

1.5 仿射

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltdef main():img = cv.imread('lena.png')assert img is not None, "file could not be read, check with os.path.exists()"rows, cols, _ = img.shape# pts1 表示变换前三个点位置# pts2 表示变换后三个点位置pts1 = np.float32([[270, 270], [330, 270], [310, 320]])pts2 = np.float32([[100, 100], [150, 50], [150, 100]])M = cv.getAffineTransform(pts1, pts2)img_result = cv.warpAffine(img, M, (cols, rows))for p in pts1:cv.circle(img, center=(int(p[0]), int(p[1])), radius=3,color=(0, 0, 255), thickness=cv.FILLED)for p in pts2:cv.circle(img_result, center=(int(p[0]), int(p[1])), radius=3,color=(0, 255, 0), thickness=cv.FILLED)title = ['Origin', 'Affine']# 对应的图像imgs = [img, img_result]for i in range(len(imgs)):plt.subplot(1, len(imgs), i + 1)plt.imshow(cv.cvtColor(imgs[i], cv.COLOR_BGR2RGB))plt.title(title[i])plt.axis('off')plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_19_img_affine

1.6 透视

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltdef main():img = cv.imread('lena.png')assert img is not None, "file could not be read, check with os.path.exists()"rows, cols, _ = img.shape# 将图像投影到一个新的视平面,需要四个点, 在这4个点中,有3个点不应共线# 平面的4个点顺序为 左上 右上 左下 右下pts1 = np.float32([[270, 270], [330, 270], [270, 350], [320, 350]])pts2 = np.float32([[0, 0], [512, 0], [0, 512], [512, 512]])M = cv.getPerspectiveTransform(pts1, pts2)img_result = cv.warpPerspective(img, M, (cols, rows))for p in pts1:cv.circle(img, center=(int(p[0]), int(p[1])), radius=3,color=(0, 0, 255), thickness=cv.FILLED)for p in pts2:cv.circle(img_result, center=(int(p[0]), int(p[1])), radius=3,color=(0, 255, 0), thickness=cv.FILLED)title = ['Origin', 'Perspective',]# 对应的图像imgs = [img, img_result]for i in range(len(imgs)):plt.subplot(1, len(imgs), i + 1)plt.imshow(cv.cvtColor(imgs[i], cv.COLOR_BGR2RGB))plt.title(title[i])plt.axis('off')plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_20_img_perspective

二、低通滤波

与一维信号一样,图像也可以用各种低通滤波器(low-pass filters,LPF)、高通滤波器(high-pass filters,HPF)等进行过滤

  • LPF 用于降低某些像素强度,可以用于平滑图像,保留图像中的低频成分,过滤高频成分,帮助去除噪点,模糊图像等
  • HPF 用于增强某些像素强度,可以用于帮助寻找图像中的边缘

2.1 均值滤波

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltdef main():img = cv.imread('lenaNoise.png')assert img is not None, "file could not be read, check with os.path.exists()"# 均值滤波: 简单的平均卷积操作result = cv.blur(img, ksize=(5, 5))# 显示图像titles = ['origin image', 'blur image']images = [img, result]for i in range(2):plt.subplot(1, 2, i+1), plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.tight_layout()plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_21_blur

2.2 高斯滤波

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltdef main():img = cv.imread('lenaNoise.png')assert img is not None, "file could not be read, check with os.path.exists()"# 高斯滤波:# ksize: 高斯核大小,可以是0或正奇数,当为0从sigma计算result_0 = cv.GaussianBlur(img, ksize=(5, 5), sigmaX=0)# 显示图像titles = ['origin image', 'gaussian blur']images = [img, result_0]for i in range(len(images)):plt.subplot(1, len(images), i+1), plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.tight_layout()plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_21_gaussian_blur

2.3 中值滤波

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltdef main():img = cv.imread('lenaNoise.png')assert img is not None, "file could not be read, check with os.path.exists()"# 中值滤波:result_0 = cv.medianBlur(img, ksize=3)result_1 = cv.medianBlur(img, ksize=5)result_2 = cv.medianBlur(img, ksize=7)# 显示图像titles = ['origin image', 'ksize=3', 'ksize=5', 'ksize=7']images = [img, result_0, result_1, result_2]for i in range(len(images)):plt.subplot(2, 2, i+1), plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_21_median_filter

2.4 双边滤波

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltdef main():img = cv.imread('lenaNoise.png')assert img is not None, "file could not be read, check with os.path.exists()"# 双边滤波: 在计算像素值的同时会考虑距离和色差信息,从而可在消除噪声得同时保护边缘信息,可用于美颜# d 每个像素邻域的直径,当<=0时,从sigmaSpace计算     注意:当 d > 5 非常慢,所以建议使用=5作为实时应用,或者使用=9作为需要重噪声过滤的离线应用# sigmaColor 颜色标准方差,一般尽可能大,较大的值表示在颜色相近的区域内像素将会被更多地保留# sigmaSpace 坐标空间标准方差(像素单位),一般尽可能小,较小的值表示在距离中心像素较远的像素的权重较小result_0 = cv.bilateralFilter(img, d=0, sigmaColor=100, sigmaSpace=15)result_1 = cv.bilateralFilter(img, d=0, sigmaColor=50, sigmaSpace=15)# 显示图像titles = ['origin image', 'color=100 space=5', 'color=50 space=5']images = [img, result_0, result_1]for i in range(len(images)):plt.subplot(1, len(images), i+1), plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_22_bilateral_filter

2.5 自定义滤波

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltdef main():img = cv.imread('lenaNoise.png')assert img is not None, "file could not be read, check with os.path.exists()"# 自定义滤波,对图像进行卷积运算:# 先定义卷积核,再参与计算,ddepth=-1表示和源图像深度一致# 此外还有个参数anchor,默认值为(-1,-1)表示锚点位于内核中心kernel = np.ones((5, 5), np.float32)/25result = cv.filter2D(img, ddepth=-1, kernel=kernel)# 显示图像titles = ['origin image', 'filter2d filter',]images = [img, result]for i in range(len(images)):plt.subplot(1, len(images), i+1), plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_23_2d_filter

三、高通滤波

3.1 Sobel

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltdef sobel(img):# Sobel 使用Sobel算子计算一阶、二阶、三阶或三者混合 图像导数梯度# ddepth 输出图像的深度,计算图像的梯度会有浮点数,负数,所以后面会取绝对值# dx,dy  X和Y方向的梯度# ksize  参与图像卷积操作的核,大小可以是1、3、5或7,用于不同精度的边缘检测grad_x = cv.Sobel(img, ddepth=cv.CV_64F, dx=1, dy=0, ksize=3)abs_grad_x = cv.convertScaleAbs(grad_x)grad_y = cv.Sobel(img, ddepth=cv.CV_64F, dx=0, dy=1, ksize=3)abs_grad_y = cv.convertScaleAbs(grad_y)# 分别计算x和y,再求和sobel = cv.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0)return abs_grad_x, abs_grad_y, sobeldef main():img = cv.imread('res2.jpg')img_lena = cv.imread('lena.png', cv.IMREAD_GRAYSCALE)assert img is not None and img_lena is not None, "file could not be read, check with os.path.exists()"# 显示图像titles = ['origin ', 'sobel_x', 'sobel_y', 'sobel_x+sobel_y', 'lena ','lena_sobel_x', 'lena_sobel_y', 'lena_sobel_x+lena_sobel_y']images = [img, *sobel(img), img_lena, *sobel(img_lena)]for i in range(len(images)):plt.subplot(2, 4, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_26_sobel

3.2 Scharr

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']def main():img = cv.imread('lena.png', cv.IMREAD_GRAYSCALE)assert img is not None, "file could not be read, check with os.path.exists()"scharrx = cv.Scharr(img, ddepth=cv.CV_64F, dx=1, dy=0)scharrxAbs = cv.convertScaleAbs(scharrx)scharry = cv.Scharr(img, ddepth=cv.CV_64F, dx=0, dy=1)scharryAbs = cv.convertScaleAbs(scharry)# 分别计算x和y,再求和scharrxy = cv.addWeighted(scharrxAbs, 0.5, scharryAbs, 0.5, 0)# 显示图像titles = ['origin ', 'scharrx', 'scharry', 'scharrx + scharry']images = [img, scharrxAbs, scharryAbs, scharrxy]for i in range(len(images)):plt.subplot(2, 2, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.tight_layout()plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_27_scharr

3.3 Laplacian

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']def main():img = cv.imread('lena.png', cv.IMREAD_GRAYSCALE)assert img is not None, "file could not be read, check with os.path.exists()"result = cv.Laplacian(img, cv.CV_16S, ksize=3)resultAbs = cv.convertScaleAbs(result)# 显示图像titles = ['origin ', 'Laplacian']images = [img, resultAbs]for i in range(len(images)):plt.subplot(1, 2, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_28_laplacian

3.4 Canny

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# plt.rcParams['font.sans-serif'] = ['SimHei']def main():img = cv.imread('lena.png')assert img is not None, "file could not be read, check with os.path.exists()"# canny 是一个多步算法,先用5*5核高斯滤波过滤噪声,然后用Sobel算法查找图像梯度,最后去除任何可能不构成边缘的、不需要的像素# threshold1 较小值 低于这个值的肯定不是边# threshold2 较大值 高于这个值的肯定是边# 位于两者之间的需要进行连通性判断img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)img_result_1 = cv.Canny(img_gray, threshold1=100, threshold2=200)img_result_2 = cv.Canny(img_gray, threshold1=50, threshold2=240)# 显示图像titles = ['origin ', 'gray', 'canny 100,200', 'canny 50,240']images = [img, img_gray, img_result_1, img_result_2]for i in range(len(images)):plt.subplot(2, 2, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.tight_layout()plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_29_canny

四、图像金字塔

通常,我们通常处理恒定大小的图像。但在某些情况下,我们需要处理不同分辨率的(相同的)图像。例如,当在图像中搜索某个东西时,比如人脸,我们不确定物体将在该图像中出现在什么大小。在这种情况下,我们将需要创建一组具有不同分辨率的相同图像,并在所有这些图像中搜索对象。这些不同分辨率的图像集被称为图像金字塔(因为当它们被保存在一个堆栈中,顶部是最高分辨率的图像时,它看起来就像一个金字塔)。

金字塔的一个应用是图像混合。

4.1 高斯金字塔

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']def main():img = cv.imread('lena.png')assert img is not None, "file could not be read, check with os.path.exists()"# 模糊图像向下采样img_down = cv.pyrDown(img)img_down_down = cv.pyrDown(img_down)img_down_down_down = cv.pyrDown(img_down_down)# 显示图像titles = ['origin ', '向下采样1', '向下采样2', '向下采样3']images = [img, img_down, img_down_down, img_down_down_down]for i in range(len(images)):plt.subplot(2, 2, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.tight_layout()plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_30_gaussian_pyramid

4.2 拉普拉斯金字塔

拉普拉斯金字塔是由高斯金字塔形成的。拉普拉斯金字塔图像像边缘图像,它的大多数元素都是零,常被用于图像压缩。拉普拉斯金字塔的水平是由高斯金字塔的水平与高斯金字塔的扩展水平的差异形成的

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# plt.rcParams['font.sans-serif'] = ['SimHei']def main():img = cv.imread('lena.png')assert img is not None, "file could not be read, check with os.path.exists()"# pyrDown和pyrUp并不是可逆的img_down = cv.pyrDown(img)img_down_up = cv.pyrUp(img_down)img_laplacian = img - img_down_up# 显示图像titles = ['origin ', 'img_laplacian']images = [img, img_laplacian]for i in range(len(images)):plt.subplot(1, 2, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_30_laplacian_pyramid

五、形态学

5.1 腐蚀

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# plt.rcParams['font.sans-serif'] = ['SimHei']def main():img = cv.imread('j.png')assert img is not None, "file could not be read, check with os.path.exists()"kernel3 = np.ones((3, 3), np.uint8)img_erode_3 = cv.erode(img, kernel3, iterations=1)kernel5 = np.ones((5, 5), np.uint8)img_erode_5 = cv.erode(img, kernel5, iterations=1)cross = cv.getStructuringElement(cv.MORPH_CROSS, (5, 5))img_erode_by_cross = cv.erode(img, cross)ellipse = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5))img_erode_by_ellipse = cv.erode(img, ellipse)# 显示图像titles = ['origin ', 'erode_3', 'erode_5', 'cross', 'ellipse']images = [img, img_erode_3, img_erode_5,img_erode_by_cross, img_erode_by_ellipse]for i in range(len(images)):plt.subplot(2, 3, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.tight_layout()plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_31_erode

5.2 膨胀

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# plt.rcParams['font.sans-serif'] = ['SimHei']def main():img = cv.imread('j.png')assert img is not None, "file could not be read, check with os.path.exists()"kernel3 = np.ones((3, 3), np.uint8)img_dilate_3 = cv.dilate(img, kernel3, iterations=1)kernel5 = np.ones((5, 5), np.uint8)img_dilate_5 = cv.dilate(img, kernel5, iterations=1)cross = cv.getStructuringElement(cv.MORPH_CROSS, (5, 5))img_dilate_by_cross = cv.dilate(img, cross)ellipse = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5))img_dilate_by_ellipse = cv.dilate(img, ellipse)# 显示图像titles = ['origin ', 'dilate_3', 'dilate_5', 'cross', 'ellipse']images = [img, img_dilate_3, img_dilate_5,img_dilate_by_cross, img_dilate_by_ellipse]for i in range(len(images)):plt.subplot(2, 3, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.tight_layout()plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_33_dilate

5.3 运算

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# plt.rcParams['font.sans-serif'] = ['SimHei']def main():img = cv.imread('j.png')assert img is not None, "file could not be read, check with os.path.exists()"# 形态学通常用于二值化图像# 开操作:先腐蚀后膨胀# 闭操作:先膨胀后腐蚀# 形态学梯度:膨胀减去腐蚀。# 顶帽:原图像与开操作图像之间的差值图像。# 黑帽:闭操作图像与原图像之间的差值图像。kernel = np.ones((3, 3), np.uint8)img_opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)img_closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)img_gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)img_tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)img_blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)# 显示图像titles = ['origin ', 'open', 'close', 'gradient', 'tophat', 'blackhat']images = [img, img_opening, img_closing,img_gradient, img_tophat, img_blackhat]for i in range(len(images)):plt.subplot(2, 3, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.tight_layout()plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_34_morphology

六、直方图

6.1 计算

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# plt.rcParams['font.sans-serif'] = ['SimHei']def main():img = cv.imread('lena.png')assert img is not None, "file could not be read, check with os.path.exists()"img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)img_hist = cv.calcHist([img], [0], None, [256], [0, 256])# 显示图像titles = ['gray', 'origin']images = [img_gray, img]for i in range(len(images)):plt.subplot(2, 2, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.subplot(2, 2, 3)plt.plot(img_hist)plt.title('histogram')plt.xlim([0, 256])color = ('b', 'g', 'r')plt.subplot(2, 2, 4)for i, col in enumerate(color):histr = cv.calcHist([img], [i], None, [256], [0, 256])plt.plot(histr, color=col)plt.xlim([0, 256])plt.title('color histogram')plt.tight_layout()plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_35_histpgram

6.2 均衡

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# plt.rcParams['font.sans-serif'] = ['SimHei']def main():img = cv.imread('lena.png')assert img is not None, "file could not be read, check with os.path.exists()"img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)img_hist = cv.calcHist([img_gray], [0], None, [256], [0, 256])# 均衡灰度图像img_gray_equ = cv.equalizeHist(img_gray)img_equ_hist = cv.calcHist([img_gray_equ], [0], None, [256], [0, 256])clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))img_clahe = clahe.apply(img_gray)# 显示图像titles = ['origin ', 'gray', 'equ', 'clahe']images = [img, img_gray, img_gray_equ, img_clahe]for i in range(len(images)):plt.subplot(3, 2, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.subplot(3, 2, 5)plt.plot(img_hist)plt.title('orgin hist')plt.xlim([0, 256])plt.subplot(3, 2, 6)plt.plot(img_equ_hist)plt.title('equ hist')plt.xlim([0, 256])plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_36_histpgram_equalize

6.3 反向投影

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# plt.rcParams['font.sans-serif'] = ['SimHei']def main():img = cv.imread('lena.png')img_2 = cv.imread('lena.png')assert img is not None, "file could not be read, check with os.path.exists()"img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)img_hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)img_hist = cv.calcHist([img_hsv], [0], None, [256], [0, 256])cv.normalize(img_hist, img_hist, 0, 255, cv.NORM_MINMAX)# 直方图反投影 将每个像素用概率表示 经常用于图像分割或在图像中查找感兴趣的对象, 和 camshift meanshift等算法一起使用dst = cv.calcBackProject([img_hsv], [0, 1], img_hist, None, 1)# 显示图像titles = ['origin ', 'gray', 'backProject']images = [img, img_gray, dst]for i in range(len(images)):plt.subplot(2, 2, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_37_histpgram_backprojection

七、轮廓

7.1 查找显示

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# plt.rcParams['font.sans-serif'] = ['SimHei']def main():img = cv.imread('box.jpg')img_2 = img.copy()assert img is not None, "file could not be read, check with os.path.exists()"img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)ret, thresh = cv.threshold(img_gray, 127, 255, 0)# 进行查找轮廓的图片应该是二值化图片,黑色背景中找白色物体轮廓contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)cv.drawContours(img_2, contours, -1, (0, 255, 0), 3)# cv.drawContours(img_2, contours, 3, (0, 255, 0), 3)# cnt = contours[4]# cv.drawContours(img_2, [cnt], 0, (0, 255, 0), 3)# 显示图像titles = ['origin ', 'binary', 'cornor']images = [img, thresh, img_2]for i in range(len(images)):plt.subplot(2, 2, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_38_contour_find_draw

7.2 常用特征

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# plt.rcParams['font.sans-serif'] = ['SimHei']def main():img = cv.imread('g.png')assert img is not None, "file could not be read, check with os.path.exists()"img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)ret, thresh = cv.threshold(img_gray, 127, 255, 0)# 进行查找轮廓的图片应该是二值化图片,黑色背景中找白色物体轮廓contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)cnt = contours[0]M = cv.moments(cnt)cx = int(M['m10'] / M['m00'])cy = int(M['m01'] / M['m00'])print('重心:', cx, cy)area = cv.contourArea(cnt)print('面积:', area)perimeter = cv.arcLength(cnt, True)print('周长:', perimeter)# 轮廓近似图像img_approx = img.copy()epsilon = 0.01*cv.arcLength(cnt, True)approx = cv.approxPolyDP(cnt, epsilon, True)cv.drawContours(img_approx, [approx], -1, (0, 255, 0), 2)# 外接矩形img_rect = img.copy()x, y, w, h = cv.boundingRect(cnt)cv.rectangle(img_rect, (x, y), (x+w, y+h), (0, 255, 0), 2)# 最小矩形img_min_rect = img.copy()rect = cv.minAreaRect(cnt)box = cv.boxPoints(rect)box = np.int0(box)cv.drawContours(img_min_rect, [box], 0, (0, 0, 255), 2)# 外接圆img_circle = img.copy()(x, y), radius = cv.minEnclosingCircle(cnt)center = (int(x), int(y))radius = int(radius)cv.circle(img_circle, center, radius, (0, 255, 0), 2)# 凸包img_hull = img.copy()hull = cv.convexHull(cnt)cv.drawContours(img_hull, [hull], 0, (0, 255, 0), 2)# 椭圆img_ellipse = img.copy()ellipse = cv.fitEllipse(cnt)cv.ellipse(img_ellipse, ellipse, (0, 255, 0), 2)# 线img_line = img.copy()rows, cols = img.shape[:2][vx, vy, x, y] = cv.fitLine(cnt, cv.DIST_L2, 0, 0.01, 0.01)lefty = int((-x*vy/vx) + y)righty = int(((cols-x)*vy/vx)+y)cv.line(img_line, (cols-1, righty), (0, lefty), (0, 255, 0), 2)cv.drawContours(img, contours, -1, (0, 255, 0), 2)# 显示图像titles = ['binary', 'cornor', 'approx','rect', 'min_rect', 'circle', 'hull', 'ellipse', 'line']images = [thresh, img, img_approx, img_rect,img_min_rect, img_circle, img_hull, img_ellipse, img_line]for i in range(len(images)):plt.subplot(3, 3, i+1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')plt.tight_layout()plt.show()cv.waitKey(0)cv.destroyAllWindows()if __name__ == '__main__':main()

demo_38_contour_feature

参考

  1. https://github.com/LeBron-Jian/ComputerVisionPractice
http://www.lryc.cn/news/362937.html

相关文章:

  • 使用若依框架RuoYi前后端分离版+运行+自动生成页面进行导入进行开发+工具(完整版)
  • 开源博客项目Blog .NET Core源码学习(29:App.Hosting项目结构分析-17)
  • 2021 hnust 湖科大 计组课设 包含multisim14连线文件,报告,指导书
  • CHATGPT升级plus(已有账号前提下)
  • 通过血清拉曼光谱进行COVID-19的高效初步筛查
  • Java 性能调优与监控工具详解
  • LeetCode 1633, 122, 239
  • (十五)统计学基础练习题九(选择题T401-450)
  • 用大白话讲解下 CNN和卷积核( 百度 AI 回答 )
  • 安全生产新篇章:可燃气体报警器检验周期的国家标准解读
  • github搭建个人博客
  • Linux系统之mv命令的基本使用
  • vscode 好用的插件
  • 什么是蓝牙定位车载GPS终端
  • 指纹采集技术
  • /etc/fstab、/etc/mtab 文件详解及永久挂载(文件系统、ISO镜像、文件网络共享)
  • 【Linux】进程(5):命令行参数
  • vue2+antv/x6实现er图
  • 如何在XDMA中查看LTSSM状态机
  • 编译和运行qemu-uboot-arm64单板的Armbian系统
  • Python版《消消乐》,附源码
  • Kubernetes ingress
  • 【JavaScript】ECMAS6(ES6)新特性概览(二):解构赋值、扩展与收集、class类全面解析
  • Linux入门学习指南
  • 纯血鸿蒙实战开发—如何添加顶部tab页面
  • 数仓建模—指标拆解和选取
  • HTML静态网页成品作业(HTML+CSS)——VIVO介绍网页(1个页面)
  • MySQL(四) - SQL优化
  • 用 DataGridView 控件显示数据
  • VisualSVN Server/TortoiseSVN更改端口号