欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Python-Opencv 基本操作

程序员文章站 2022-04-07 14:08:52
下面完整代码在github仓库:传送门文章目录一、仿射变换二、直方图反向投影三、DFT离散傅里叶变换四、绘制直方图五、图像翻转、缩放六、均值滤波、中值滤波、高斯滤波、双边滤波七、锐化操作(凸显轮廓)八、Sobel算子(找轮廓)九、Scharr算子(找轮廓)十、双线性插值、最邻近插值、样条插值、Lanczos插值十一、图像形态学操作(膨胀、腐蚀、开、闭等)十二、高斯金字塔、拉普拉斯金字塔十三、利用图像金字塔合成苹果和橘子十四、图像透视变换一、仿射变换&...

下面完整代码在github仓库:传送门


一、仿射变换

       任意一个二维图像,我们乘以一个仿射矩阵,就能得到仿射变换后的图像。变换包含:缩放、旋转、平移、倾斜、镜像。
Python-Opencv 基本操作

import cv2
import numpy as np

# 图像仿射变换
img = cv2.imread("./images/6.jpg")
rows, cols, channels = img.shape  # h, w, c

#  创建仿射变换矩阵M
M0 = np.float32([[1, 0, 0], [0, 1, 0]])
M1 = np.float32([[1, 0, 20], [0, 1, 80]])  # 沿x轴平移+20, 沿y轴平移+80
M2 = np.float32([[0.8, 0, 20], [0, 0.5, 80]])   # 沿x轴变为原来的0.8, y轴变为原来的0.5
# M3 = np.float32([[np.sqrt(3)/2, 0.5, 0], [-0.5, np.sqrt(3)/2, 0]])  # 逆时针旋转30度
M3 = cv2.getRotationMatrix2D((cols//2, rows//2), 45, scale=0.8)  # 按照中心点逆时针旋转45度,图片大小为原来的0.8倍。
M4 = np.float32([[1, 0.5, 0], [0, 1, 0]])  # 沿x轴倾斜0.5倍
M5 = np.float32([[1, 0, 0], [0.5, 1, 0]])  # 沿y轴倾斜0.5倍
# M6 = np.float32([[-1, 0, cols], [0, 1, 0]])  # 绕y轴翻转,沿x轴平移cols个像素单位。
M6 = np.float32([[1, 0, 0], [0, -1, rows]])  # 沿x轴翻转,沿y轴平移rows个像素单位。
M7 = np.float32([[-1, 0, cols], [0, -1, rows]])  # 绕y转翻转、绕x转翻转,最后沿x轴平移cols个像素单位、沿y轴平移rows个像素单位


# 进行仿射变换操作,dsize:指定输出图片的大小
dst0 = cv2.warpAffine(img, M0, dsize=(cols, rows))  # 图形没有变换
dst1 = cv2.warpAffine(img, M1, dsize=(cols, rows))  # 平移
dst2 = cv2.warpAffine(img, M2, dsize=(cols, rows))  # 缩放
dst3 = cv2.warpAffine(img, M3, dsize=(cols, rows))  # 旋转
dst4 = cv2.warpAffine(img, M4, dsize=(cols*2, rows*2))  # 倾斜
dst5 = cv2.warpAffine(img, M5, dsize=(cols*2, rows*2))  # 倾斜
dst6 = cv2.warpAffine(img, M6, dsize=(cols, rows))  # 翻转/镜像
dst7 = cv2.warpAffine(img, M7, dsize=(cols, rows))  # 翻转/镜像


cv2.imshow("img", img)
# cv2.imshow("dst0", dst0)
# cv2.imshow("dst1", dst1)
# cv2.imshow("dst2", dst2)
# cv2.imshow("dst3", dst3)
# cv2.imshow("dst4", dst4)
# cv2.imshow("dst5", dst5)
# cv2.imshow("dst6", dst6)
cv2.imshow("dst7", dst7)
cv2.waitKey(0)
cv2.destroyAllWindows()

二、直方图反向投影

import cv2

# 直方图反向投影
# 感兴趣对象ROI (要抠取哪部分图像)
roi = cv2.imread("./images/30.jpg")
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)  # 转化为HSV图

# 目标图像
target = cv2.imread("./images/16.jpg")
hsv_target = cv2.cvtColor(target, cv2.COLOR_BGR2HSV)  # 转化为HSV图

# 统计ROI的直方图
hist_roi = cv2.calcHist([hsv_roi], [0, 1], None, [180, 256], [0, 179, 0, 255])
'''cv2.calcHist(images,channels,mask,histSize,ranges[,hist[,accumulate]])

images:必须用括号括起来表示,即[image]
chanels:用于计算直方图的通道,使用灰度图计算直方图,所以就直接使用第一个通道;对于彩色可以通过[0],[1],[2]分别计算蓝色、绿色或红色通道的直方图
mask:如果计算完整图像的直方图,它将设为None。但是如果想找到图像特定区域的直方图,则必须为它创建一个蒙版图像作为蒙版。
histSize:表示这个直方图分成多少份(即多少个直方柱)。对于满量程,设置为[256]
ranges:表示直方图中各个像素的值,通常设为[0,256]
'''


# ROI的直方图归一化:在调用calBackProject之前,需要对hist_roi进行归一化
cv2.normalize(hist_roi, hist_roi, 0, 255, cv2.NORM_MINMAX)

# 反向投影:calBackProject
backProject = cv2.calcBackProject([hsv_target], [0, 1], hist_roi, [0, 179, 0, 255], 1)

# 圆盘卷积
kernel = cv2.getStructuringElement(shape=cv2.MORPH_ELLIPSE, ksize=(5, 5))
backProject = cv2.filter2D(backProject, -1, kernel)

# 图像二值化
ret, image = cv2.threshold(backProject, 50, 255, cv2.THRESH_BINARY)

# 抠出目标图像中的感兴趣部分
merge_image = cv2.merge((image, image, image))  # 合并图像的三个通道
res = cv2.bitwise_and(target, merge_image)

cv2.imshow("roi", roi)
cv2.imshow("target", target)
cv2.imshow("hist_roi", hist_roi)
cv2.imshow("backProject", backProject)
cv2.imshow("merge image", merge_image)
cv2.imshow("res", res)

cv2.waitKey(0)
cv2.destroyAllWindows()

三、DFT离散傅里叶变换

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

# opencv中的DFT(Discrete Fourier Transform)
img = cv2.imread("./images/1.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
rows, cols = gray.shape

# 1.DFT离散傅里叶变换:空域-->频域
dft = cv2.dft(src=np.float32(gray), flags=cv2.DFT_COMPLEX_OUTPUT)  # cv2.DFT_COMPLEX_OUTPUT表示进行傅里叶变化的方法
print(dft.shape)  # (540, 960, 2)  2表示两个通道

# 2.中心化:将低频移动到图像中心
fftshift = np.fft.fftshift(dft)
# 获取振幅谱(展示图片用):20*np.log()是为了将值限制在[0, 255]
magnitude_spectrum = 20 * np.log(cv2.magnitude(fftshift[:, :, 0], fftshift[:, :, 1]))

# 3.滤波操作之低通滤波(去高频,保低频)
mask = np.zeros((rows, cols, 2), dtype=np.uint8)
mask[(rows//2 - 30):(rows//2+30), (cols//2-30):(cols//2+30)] = 1
fftshift = fftshift * mask

# 4.去中心化:将低频和高频的位置还原
ifftshift = np.fft.ifftshift(fftshift)

# 5.逆傅里叶变换:频域-->空域
idft = cv2.idft(ifftshift)

# 6.二维向量取模(幅值)
img_back = cv2.magnitude(idft[:, :, 0], idft[:, :, 1])

titles = ['Gray', 'magnitude spectrum', 'img_back']
images = [gray, magnitude_spectrum, img_back]
for i in range(3):
    plt.subplot(2, 2, i+1)
    plt.imshow(images[i], cmap="gray")
    plt.title(titles[i])
    plt.xticks([])
    plt.yticks([])

plt.subplot(224), plt.imshow(img_back), plt.title("Result in JET")  # 默认cmap为‘jet’
plt.show()

四、绘制直方图

import cv2
import matplotlib.pyplot as plt

# 绘制直方图
# 一维直方图
img = cv2.imread("./images/30.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转化为灰度图

# 绘制灰度图的直方图
hist_gray = cv2.calcHist(images=[gray], channels=[0], mask=None, histSize=[256], ranges=[0, 255])  # 256表示直方图的个数。

# 绘制单通道B的直方图
hist_B = cv2.calcHist([img], [0], None, [256], [0, 255])

# 绘制单通道G的直方图
hist_G = cv2.calcHist([img], [1], None, [256], [0, 255])

# 绘制单通道R的直方图
hist_R = cv2.calcHist([img], [2], None, [256], [0, 255])

plt.plot(hist_gray, color="gray", label="Gray")
plt.plot(hist_B, color="b", label="B")
plt.plot(hist_G, color='g', label="G")
plt.plot(hist_R, color='r', label="R")
plt.show()
import cv2
import matplotlib.pyplot as plt

# 绘制2D直方图
img = cv2.imread("./images/1.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)  # 转为HSV图

hist_hsv = cv2.calcHist([hsv], [0, 1], None, [180, 255], [0, 180, 0, 255])

# cv2.namedWindow("hist_hsv", cv2.WINDOW_NORMAL)
cv2.imshow("hist_hsv1", hist_hsv)
cv2.waitKey(0)
cv2.destroyAllWindows()

五、图像翻转、缩放

import cv2

# 图像翻转
src = cv2.imread("./images/1.jpg")
dst = cv2.transpose(src)  # h, w, c --> w, h, c

cv2.imshow("", src)
cv2.imshow("", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2

# 翻转:flipCode用来控制翻转效果
src = cv2.imread("./images/1.jpg")

dst1 = cv2.flip(src, flipCode=0)  # flipcode=0: 绕x轴翻转,即上下颠倒
# dst2 = cv2.flip(src, flipCode=1)  # flipcode=1: 绕y轴翻转,即左右翻转
# dst3 = cv2.flip(src, flipCode=-1)  # flipcode=-1: 绕x,y轴同时翻转,即上下加左右翻转。

cv2.imshow("src", src)

cv2.imshow("dst1", dst1)
# cv2.imshow("dst2", dst2)
# cv2.imshow("dst3", dst3)

cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2

img = cv2.imread("./images/1.jpg")
print(img.shape)
rows, cols, channels = img.shape

# 图片缩放:resize()
resize_1 = cv2.resize(img, dsize=(cols*2, rows*2))  # 按尺寸, 图像扩大两倍
resize_2 = cv2.resize(img, dsize=(12, 45), fx=1.2, fy=2)  # 若dsize为负数或零,按比例因子进行缩放,否则就按照dsize所给的尺寸。
# print(resize_1.shape)
print(resize_2.shape)

# cv2.imshow("resize_1", resize_1)
# cv2.imshow("resize_2", resize_2)
cv2.waitKey(0)
cv2.destroyAllWindows()

六、均值滤波、中值滤波、高斯滤波、双边滤波

import cv2
import numpy as np

'''
    图像的时域(分析每个时间点、空间位置的一个点)和频域(分析两个点、线之间的变化:梯度)
    滤波是将信号中特定波段频率滤除的操作,是抑制和防止干扰的一项重要措施。
    图像滤波是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。
    图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制。
    图像滤波的目的:
    1.消除图像中混入的噪声;
    2.为图像识别抽取出图像特征。
    滤波可分为 低通滤波、高通滤波、中通滤波、阻带滤波。都是从频域上区别的。
    低通滤波/平滑滤波:减弱或阻隔高频信号,保留低频信号,只留下变化较小的信号。可使图像变模糊,主要用于去噪。
    高通滤波:减弱或阻隔低频信号,保留高频信号,只留下变化较大的信号。一般用于获取图像边缘、轮廓或梯度。
    中通滤波:获取已知频率范围内的信号,去掉变化较大和较小的信号,留下变化适中的信号。
    阻带滤波:去掉已知频率范围内的信号,去掉变化适中的信号,留下变化较大和较小的信号。
'''

# 卷积滤波
img = cv2.imread("./images/1.jpg")

# 定义一个卷积核
kernel = np.float32([[1, 1, 0], [1, 0, -1], [0, -1, -1]])
# kernel = np.float32([[-1, 1, 0], [1, 0, -1], [0, -1, 1]])
# kernel = np.float32([[1, 0, 0], [0, 0, 0], [0, 0, -1]])

dst = cv2.filter2D(img, -1, kernel)  # -1表示在所有通道做卷积

# cv2.imshow("image", img)
# cv2.imshow("dst", dst)
# cv2.waitKey(0)
# cv2.destroyAllWindows()

img1 = cv2.imread("./images/132.jpg")
img2 = cv2.imread("./images/134.jpg")

# 1.均值滤波:blur(src, ksize, dst=None, anchor=None, borderType=None)
mean = cv2.blur(img1, ksize=(3, 3))

# 2.中值滤波:medianBlur(src, ksize, dst=None), 其中ksize为大于1的奇数
median = cv2.medianBlur(img1, ksize=7)

# 3.高斯滤波:GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None)
# ksize为奇数,不知道噪声分布的情况下一般使用高斯滤波
# sigmaX是高斯函数在x轴方向上的标准差。若不指定sigmaY,则sigmaY=sigmaX。
gaussian = cv2.GaussianBlur(img1, ksize=(7, 7), sigmaX=3, sigmaY=3)

# 4.双边滤波:bilateralFilter(src, d, sigmaColor, sigmaSpace, dst=None, borderType=None)
# d是每个像素的领域直径,由两个函数组成
# sigmaColor:在颜色空间中的过滤sigma。sigmaSpace:在坐标系空间中的过滤sigma。
# sigma为无穷大时,效果等价于高斯模糊。sigma为0时,与原图一样。
bilateral = cv2.bilateralFilter(img2, 33, 77, 77)
# bilateral = cv2.bilateralFilter(img1, 9, 0, 0)

# 高斯滤波和双边滤波的区别:
# 1)高斯核只考虑了空间分布,没有考虑到像素值的差异,会将图像的边缘模糊掉。
# 2)双边滤波是基于高斯滤波提出的,结合了图像的空间邻近度和像素值相似度的一种折中处理,具有保边特性。

cv2.imshow("img1", img1)
cv2.imshow("img2", img2)
cv2.imshow("mean", mean)
cv2.imshow("median", median)
cv2.imshow("gaussian", gaussian)
cv2.imshow("bilateral", bilateral)
cv2.waitKey(0)
cv2.destroyAllWindows()

七、锐化操作(凸显轮廓)

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

# 高通滤波(低通滤波处理背景信息,高通滤波处理轮廓信息)
src = cv2.imread("./images/1.jpg")

# 锐化操作(作用是将轮廓凸显出来)
# 1.自定义锐化核
kernel = np.float32([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])
dst1 = cv2.filter2D(src, -1, kernel)

# 2.USM锐化(UnsharpMask)
gaussian = cv2.GaussianBlur(src, (7, 7), 7)
dst2 = cv2.addWeighted(src, 2, gaussian, -1, 0)  # 2*src-gaussian*(-1)

cv2.imshow("src", src)
cv2.imshow("dst1", dst1)
cv2.imshow("dst2", dst2)
cv2.waitKey(0)
cv2.destroyAllWindows()

八、Sobel算子(找轮廓)

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

# 梯度操作/高通滤波:找轮廓
src = cv2.imread("./images/6.jpg")  # (201, 200, 3)
gray = cv2.imread("./images/6.jpg", cv2.IMREAD_GRAYSCALE)  # 形状为(201, 200)

# Sobel算子: dx和dy表示的是求导的阶数,0表示这个方向上没有求导,一般为0、1、2。
sobel_x = cv2.Sobel(gray, -1, dx=1, dy=0, ksize=3)  # x轴方向上的一阶导数
sobel_y = cv2.Sobel(gray, -1, dx=0, dy=1, ksize=3)  # y轴方向上的一阶导数

sobel_x_abs = cv2.convertScaleAbs(sobel_x, alpha=2, beta=1)
sobel_y_abs = cv2.convertScaleAbs(sobel_y, alpha=2, beta=1)

sobel_x_abs2 = np.uint8(np.sqrt(sobel_x**2))
sobel_y_abs2 = np.uint8(np.sqrt(sobel_y**2))

sobel = cv2.addWeighted(sobel_x_abs, 0.5, sobel_y_abs, 0.5, 0)

# 结合matplotlib显示多张图片
titles = ['Original Gray', 'Sobel x', 'Sobel y', 'Sobel x abs', 'Sobel y abs', 'Sobel x abs2', 'Sobel y abs2', 'sobel']
images = [gray, sobel_x, sobel_y, sobel_x_abs, sobel_y_abs, sobel_x_abs2, sobel_y_abs2, sobel]
for i in range(8):
    plt.subplot(2, 4, i+1)
    plt.imshow(images[i], cmap="gray")
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])

plt.show()

'''
函数cv2.convertScaleAbs(src[,alpha[,beta]])

概述:
先计算数组绝对值,后转化为8位无符号数

参数:
src:输入图像(多维数组)
alpha:比例因子
beta:保存新图像(数组)前可以增加的值
'''

九、Scharr算子(找轮廓)

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

# 梯度操作/高通滤波:找轮廓
src = cv2.imread("./images/6.jpg")  # (201, 200, 3)
gray = cv2.imread("./images/6.jpg", cv2.IMREAD_GRAYSCALE)  # 形状为(201, 200)

# Scharr算子:dx和dy表示的是求导的阶数,0表示这个方向上没有导数, 一般为0,1,2
scharr_x = cv2.Scharr(gray, -1, dx=1, dy=0)  # x轴方向上的一阶导数
scharr_y = cv2.Scharr(gray, -1, dx=0, dy=1)  # y轴方向上的一阶导数

scharr_x_abs = cv2.convertScaleAbs(scharr_x)
scharr_y_abs = cv2.convertScaleAbs(scharr_y)
charr = cv2.addWeighted(scharr_x_abs, 0.5, scharr_y_abs, 0.5, 0)  # 近似有|G|=|Gx|+|Gy|

# Laplacian算子
laplacian = cv2.Laplacian(gray, -1)

title = ['Original image', 'Scharr_x', 'Scharr_y', 'Scharr_x_abs', 'Scharr_y_abs', 'Charr', 'Laplacian']
images = [gray, scharr_x, scharr_y, scharr_x_abs, scharr_y_abs, charr, laplacian]
for i in range(7):
    plt.subplot(2, 4, i+1)
    plt.imshow(images[i], cmap="gray")
    plt.title(title[i])
    plt.xticks([]), plt.yticks([])

plt.show()

十、双线性插值、最邻近插值、样条插值、Lanczos插值

import cv2
import time

img = cv2.imread("./images/1.jpg")
rows, cols, channels = img.shape

# 几种插值方式:
# 1.双线性插值(默认)(相较于最邻近插值更加平滑)
linear = cv2.resize(img, dsize=(cols*2, rows*2), interpolation=cv2.INTER_LINEAR)

start_time = time.time()
# 2.最邻近插值(速度最快,但不清晰,有马赛克)
nearest = cv2.resize(img, dsize=(cols*2, rows*2), interpolation=cv2.INTER_NEAREST)
end_time = time.time()

# 3.基于4*4像素三次样条插值
start_time2 = time.time()
cubic = cv2.resize(img, dsize=(cols*2, rows*2), interpolation=cv2.INTER_CUBIC)
end_time2 = time.time()

# 4.基于8*8像素领域内的Lanczos插值 (缺点速度慢,但相较于双线性插值更清晰)
lanczos = cv2.resize(img, dsize=(cols*2, rows*2), interpolation=cv2.INTER_LANCZOS4)

# 5.基于局部像素的重采样:图像放大时,和邻近插值一样,图像缩小时,和线性插值一样。
area = cv2.resize(img, dsize=(cols*2, rows*2), interpolation=cv2.INTER_AREA)

time_use1 = end_time-start_time
time_use2 = end_time2-start_time2
print("最邻近插值所用时间:", time_use1)
print("样条插值所用时间:", time_use2)

cv2.imshow("linear", linear)
cv2.imshow("nearest", nearest)
cv2.imshow("cubic", cubic)
cv2.imshow("lanczos", lanczos)
# cv2.imshow("area", area)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 几种常用方法的效率是:最邻近插值>双线性插值>双立方插值>lanczos插值
# 但是效率和效果成反比,所以根据情况酌情使用。

十一、图像形态学操作(膨胀、腐蚀、开、闭等)

import cv2
import matplotlib.pyplot as plt

'图像形态学操作'
img1 = cv2.imread("./images/10.jpg", 0)
img2 = cv2.imread("./images/11.jpg", 0)

'''
    1.构造一个特定形状和大小的结构元素(核),用于形态学操作。
    kernel = getStructuringElement(shape, ksize, anchor=None)
    参数:
    shape: 核的形状。
        MORPH_RECT = 0: 矩形
        MORPH_CROSS = 1: 交叉形
        MORPH_ELLIPSE = 2: 椭圆形
    ksize: 核的结构大小

    2.膨胀: 原图部分区域(A)与核(B)进行卷积,求局部最大值,并将局部最大值赋值给指定像素,从而增长高亮区域。
    dilate(src, kernel, dst=None, anchor=None, iterations=None, borderType=None, borderValue=None)

    3.腐蚀: 与膨胀相反,用局部极小值替换当前像素,从而缩短高亮区域。
    erode(src, kernel, dst=None, anchor=None, iterations=None, borderType=None, borderValue=None)

    4.更多形态学操作
    morphologyEx(src, op, kernel, dst=None, anchor=None, iterations=None, borderType=None, borderValue=None)
    参数:
    op: 形态学操作类型。
        cv2.MORPH_DILATE: 膨胀。-->增长高亮部分。
        cv2.MORPH_ERODE: 腐蚀。-->缩短高亮部分。
        cv2.MORPH_GRADIENT: 梯度,(膨胀-腐蚀)。-->提取轮廓。
        cv2.MORPH_OPEN: 开,先腐蚀再膨胀。-->去除噪点。
        cv2.MORPH_CLOSE: 闭,先膨胀再腐蚀。-->填补漏洞。
        cv2.MORPH_TOPHAT: 顶帽/礼帽,(原图-开)。-->获取噪点。
        cv2.MORPH_BLACKHAT: 黑帽,(闭-原图)。-->获取漏洞。
'''

# 获取指定形状和大小的结构元素(核):getStructuringElement(shape, ksize)
# kernel = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=(3, 3))  # 矩形
# kernel = cv2.getStructuringElement(shape=cv2.MORPH_CROSS, ksize=(5, 5))  # 交叉形或十字形
kernel = cv2.getStructuringElement(shape=cv2.MORPH_ELLIPSE, ksize=(5, 5))  # 椭圆形

# 形态学操作:必须是二值化图,膨胀和腐蚀的部分是白颜色。
dilate = cv2.dilate(img1, kernel)  # 膨胀
erode = cv2.erode(img1, kernel)  # 腐蚀

morph_dilate = cv2.morphologyEx(img1, cv2.MORPH_DILATE, kernel)  # 形态学膨胀
morph_erode = cv2.morphologyEx(img1, cv2.MORPH_ERODE, kernel)  # 形态学腐蚀
morph_gradient = cv2.morphologyEx(img1, cv2.MORPH_GRADIENT, kernel)  # 梯度:膨胀减去腐蚀,用于提取轮廓

morph_open = cv2.morphologyEx(img2, cv2.MORPH_OPEN, kernel)  # 开:先腐蚀后膨胀,用于去噪
morph_close = cv2.morphologyEx(img2, cv2.MORPH_CLOSE, kernel)  # 闭:先膨胀后腐蚀,用于填补漏洞
morph_tophat = cv2.morphologyEx(img2, cv2.MORPH_TOPHAT, kernel)  # 顶帽/礼帽, 原图减去开操作,用于获取噪点。
morph_blackhat = cv2.morphologyEx(img2, cv2.MORPH_BLACKHAT, kernel)  # 黑帽, 闭操作减去原图, 用于获取漏洞。

# titles = ["IMAGE", "DILATE", "ERODE", "MORPH_DILATE", "MORPH_ERODE", "MORPH_GRADIENT"]
# images = [img1, dilate, erode, morph_dilate, morph_erode, morph_gradient]
# for i in range(6):
#     plt.subplot(2, 3, i+1)
#     plt.imshow(images[i], cmap="gray")
#     plt.title(titles[i])
#     plt.xticks([])
#     plt.yticks([])
# plt.show()

titles = ["IMAGE", "MORPH_OPEN", "MORPH_CLOSE", "MORPH_TOPHAT", "MORPH_BLACKHAT"]
images = [img2, morph_open, morph_close, morph_tophat, morph_blackhat]
for i in range(5):
    plt.subplot(2, 3, i+1)
    plt.imshow(images[i], cmap="gray")
    plt.title(titles[i])
    plt.xticks([])
    plt.yticks([])
plt.show()

十二、高斯金字塔、拉普拉斯金字塔

import cv2

# 图像金字塔

# 高斯金字塔
img = cv2.imread("./images/1.jpg")
for i in range(3):
    cv2.imshow(f"img{i}", img)
    img = cv2.pyrDown(img)
    img = cv2.pyrUp(img)

cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np

# 拉普拉斯金字塔
img = cv2.imread("./images/1.jpg")
img_down = cv2.pyrDown(img)
img_up = cv2.pyrUp(img_down)
# cv2.imshow("img_up", img_up)

img_new = cv2.subtract(img, img_up)  # 结果为轮廓信息
# img_new = cv2.subtract(2*img, np.uint8(img_up))
# cv2.imshow("img_new", img_new)

# 为了更容易看清楚,做了个提高对比度的操作
img_new2 = cv2.convertScaleAbs(img_new, alpha=5, beta=3)
cv2.imshow("img_new2", img_new2)

for i in range(3):
    cv2.imshow(f"img{i}", img_new2)
    img_new2 = cv2.pyrDown(img_new2)
    img_new = cv2.pyrUp(img_new)

cv2.waitKey(0)
cv2.destroyAllWindows()

十三、利用图像金字塔合成苹果和橘子

import cv2
import numpy as np

# 图像无缝融合:使用图像金字塔创建一个新的水果,'Orapple'
A = cv2.imread("./images/14.jpg")
B = cv2.imread("./images/15.jpg")
print(A.shape)  # (256, 256, 3)
print(B.shape)  # (256, 256, 3),两个图片大小必须一致

# 高斯金字塔
GA = A.copy()  # 复制一张苹果图片
gpA = [GA]  # 列表里面装有苹果图片数据
# print(gpA)
for i in range(6):
    GA = cv2.pyrDown(GA)
    gpA.append(GA)  # 表示列表里装了一张苹果原图和六张经过下采样的图片

print(np.shape(gpA))  # (7,)

GB = B.copy()  # 复制一张梨的图片
gbB = [GB]  # 列表里面装有苹果图片数据
for i in range(6):
    GB = cv2.pyrDown(GB)
    gbB.append(GB)  # 表示列表里装了一张梨的原图和六张经过下采样的图片

print(np.shape(gbB))


# 拉普拉斯金字塔
lpA = [gpA[6]]
print(np.shape(lpA))  # 苹果列表取最后一张图片,形状为(1, 4, 4, 3)
for i in range(6, 0, -1):
    GE = cv2.pyrUp(gpA[i])  # 从列表中取最后一张图片上采样
    L = cv2.subtract(gpA[i-1], GE)  # 拿列表中的原图片和经过上采样的图片相减,得到轮廓信息
    lpA.append(L)  # 将列表中最后一张图片加上经过处理过的六张图片。

    # cv2.imshow("L", L)
    # cv2.imshow("GE", GE)
    # cv2.imshow("gpA", gpA[i-1])
    # cv2.waitKey(0)
    cv2.destroyAllWindows()
print(np.shape(lpA))  # 一张最小的图片加上六张黑图

lpB = [gbB[6]]  #
for i in range(6, 0, -1):
    GE = cv2.pyrUp(gbB[i])
    L = cv2.subtract(gbB[i-1], GE)
    lpB.append(L)

print(lpB)


# 拼接苹果和橘子
LS = []
for la, lb in zip(lpA, lpB):
    rows, cols, channels = la.shape
    ls = np.hstack((la[:, 0:cols//2, :], lb[:, cols//2:, :]))  # 苹果,橘子的宽和高,通道合并。
    # ls = np.vstack((la[:, :cols//2], lb[:, cols//2:]))
    print(ls.shape)  # (4, 4, 3)...
    LS.append(ls)  # 将合并好的图像放在列表中

ls_ = LS[0]  # shape为(4, 4, 3)
print(ls_.shape)  # 列表存放的图片尺寸从小到大排列
for i in range(1, len(LS)):  # (1, 7)
    ls_ = cv2.pyrUp(ls_)  # 进行六次上采样
    ls_ = cv2.add(ls_, LS[i])  # 将上采样的图像和原来的带有轮廓信息的图像进行合并

cv2.imshow(f"pic {i}", ls_)  # 将合并后的最后一张图片展示出来


# 直接合成的图片
rows, cols, channels = A.shape
real = np.hstack((A[:, 0:cols//2, :], B[:, cols//2:, :]))
cv2.imshow("real", real)

cv2.waitKey(0)
cv2.destroyAllWindows()

十四、图像透视变换

import cv2
import numpy as np

# 图像透视变换
src = cv2.imread("./images/6.jpg")
rows, cols, channel = src.shape  # h, w, c
print(cols, rows)

# 获取透视变换矩阵M:getPerspectiveTransform(src, dst)
# src:源图像中四边形顶点的坐标
# dst:目标图像中对应的四边形顶点的坐标。
pts1 = np.array([[25, 30], [180, 25], [10, 190], [190, 190]], dtype=np.float32)
pts2 = np.float32([[0, 0], [200, 0], [0, 200], [200, 200]])
M = cv2.getPerspectiveTransform(pts1, pts2)

# 进行透视变换:warpPerspective(src, M, dsize)
# M: 透视变换矩阵
# dsize: 指定输出图片的大小
dst2 = cv2.warpPerspective(src, M, dsize=(200, 201))

cv2.imshow("src", src)
cv2.imshow("dst2", dst2)
cv2.waitKey(0)
cv2.destroyAllWindows()

本文地址:https://blog.csdn.net/liu1073811240/article/details/109920236