OpenCV 4.0

中文文档

GUI功能

图像入门

  • 在这里,你将学习如何读取图像、如何显示图像以及如何将其保存起来
  • 你要学习这些函数:cv.imread()、cv.imshow()、cv.imwrite()
  • 您还可以选择学习如何使用 Matplotlib 显示图像。

使用OpenCV

在Python中导入OpenCV库

import cv2 as cv

读取图像

使用 cv.imread() 函数读取一张图像,图片应该在工作目录中,或者应该提供完整的图像路径。

cv2.imread(filepath,flags)

filepath:要读入图片的完整路径

flags:指定了应该读取图像的方式

注意:

  • 你可以简单地分别传递整数 1、0 或-1,而不是这三个 flag。

看下面的代码

import numpy as np
import cv2 as cv
# 用灰度模式加载图像
img = cv.imread('messi5.jpg', cv.IMREAD_GRAYSCALE)

注意

即使图像路径错误,它也不会抛出任何错误,但是打印 img会给你None

显示图像

cv.imshow() 函数在窗口中显示图像,窗口自动适应图像的大小。

cv.imshow(window_name, image)

window_name:一个字符串,代表要在其中显示图像的窗口的名称。(==尽量不要打中文,会出现编码问题==)

image:它是要显示的图像。

你可以根据需要创建任意数量的窗口,但是窗口名字要不同。

对于imshow函数,opencv的官方注释指出:根据图像的深度,imshow函数会自动对其显示灰度值进行缩放,规则如下:

  • 如果图像数据类型是8U(8位无符号),则直接显示。
  • 如果图像数据类型是16U(16位无符号)或32S(32位有符号整数),则imshow函数内部会自动将每个像素值除以256并显示,即将原图像素值的范围由[0~255*256]映射到[0~255]
  • 如果图像数据类型是32F(32位浮点数)或64F(64位浮点数),则imshow函数内部会自动将每个像素值乘以255并显示,即将原图像素值的范围由[0~1]映射到[0~255](注意:原图像素值必须要归一化)
cv.imshow('image', img)
cv.waitKey(0)
cv.destroyAllWindows()

一个窗口的截图可能看起来像这样 (in Fedora-Gnome machine):

image-20240618135338106

cv.waitKey() 是一个键盘绑定函数,它的参数是以==毫秒==为单位的时间。该函数为任意键盘事件等待指定毫秒。如果你在这段时间内按下任意键,程序将继续。如果传的是 0,它会一直等待键盘按下。它也可以设置检测特定的击键,例如,按下键 a 等,我们将在下面讨论。

Note

  • 除了绑定键盘事件,该函数还会处理许多其他 GUI 事件,因此你必须用它来实际显示图像。

cv.destroyAllWindows() 简单的销毁我们创建的所有窗口。如果你想销毁任意指定窗口,应该使用函数 cv.destroyWindow() 参数是确切的窗口名。

创建窗口

有一种特殊情况,你可以先创建一个窗口然后加载图像到该窗口。在这种情况下,你能指定窗口是否可调整大小。它是由这个函数完成的 cv.namedWindow()

cv.namedWindow(winname, flags=None)

winname: 一个字符串,代表要在其中显示图像的窗口的名称。(==尽量不要打中文,会出现编码问题==)

flags:窗口标志。标志参数有以下几种:

默认情况下,flags 是 cv.WINDOW_AUTOSIZE,当图像尺寸太大,在窗口中添加标签是很有用的。

看下面的代码:

cv.namedWindow('image', cv.WINDOW_NORMAL)
cv.resizeWindow('image', 800, 600) # 调节窗口大小
cv.imshow('image',img)
cv.waitKey(0)
cv.destroyAllWindows()

调节窗口大小

创建窗口后设置窗口大小,设置窗口大小要使用cv.resizeWindow(),这仅适用于创建的具有除 CV_WINDOW_AUTOSIZE 以外的标志的窗口。

cv.resizeWindow(window_name, width, height)
  • window_name:将显示图像/视频的窗口的名称
  • width:新窗口宽度(整数类型)
  • height:新窗口高度(整数类型)

保存图片

保存图像,用这个函数 cv.imwrite()

retval = cv.imwrite(filename, img [, paras])

filename:要保存的文件的路径和名称,包括文件扩展名

img:要保存的 OpenCV 图像,nparray 多维数组

paras:不同编码格式的参数,==可选项==

retval:返回值,保存成功返回 True,否则返回 False。

cv.imwrite('messigray.png',img)

总结案例

下面的程序以灰度模式读取图像,显示图像,如果你按下 's‘ 会保存和退出图像,或者按下 ESC 退出不保存。

import numpy as np
import cv2 as cv

img = cv.imread('messi5.jpg',0)
cv.imshow('image',img)
k = cv.waitKey(0)
if k & 0xFF == 27: # ESC 退出
    cv.destroyAllWindows()
elif k & 0xFF == ord('s'): # 's' 保存退出
    cv.imwrite('messigray.png',img)
    cv.destroyAllWindows()

关于&0xFF的作用:

首先&运算即and运算。

0xFF是16进制数,对应的二进制数为1111 1111

当按下按键时,waitkey函数的输入值一定是一个正整数。任何一个正整数,与1111 1111做&运算,其结果必然是他本身(因为正数的补码等于原码)

查阅资料我才知道,原来系统中按键对应的ASCII码值并不一定仅仅只有8位,同一按键对应的ASCII并不一定相同(但是后8位一定相同)

为什么会有这个差别?是系统为了区别不同情况下的同一按键。

比如说q这个按键

当小键盘数字键NumLock激活时,q对应的ASCII值为100000000000001100011

而其他情况下,对应的ASCII值为01100011

相信你也注意到了,它们的后8位相同,其他按键也是如此。

为了避免这种情况,引用&0xff,正是为了只取按键对应的ASCII值后8位来排除不同按键的干扰进行判断按键是什么。

使用Matplotlib

Matplotlib 是一个 Python 的绘图库,提供了丰富多样的绘图函数。你将在接下来的文章中看到它们。在这里,你将学习如何使用 Matplotlib 来显示图像。你还能用 Matplotlib 缩放图像,保存图像等。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

img = cv.imread('messi5.jpg',0)
plt.imshow(img, cmap = 'gray', interpolation = 'bicubic')
plt.xticks([]), plt.yticks([]) # 隐藏 X 和 Y 轴的刻度值
plt.show()

注意

彩色图像 OpenCV 用的 BGR 模式,但是 Matplotlib 显示用的 RGB 模式。因此如果图像用 OpenCV 加载,则 Matplotlib 中彩色图像将无法正常显示。如下图所示(左边原图,右边是Matplotlib显示的)

image-20240618153504922

视频入门

目标

  • 学习加载视频、显示视频和保存视频。
  • 学习用相机捕捉并显示。
  • 你要学习这些函数:cv.VideoCapture(),cv.VideoWriter()

从相机捕捉视频

通常,我们用相机捕捉直播。OpenCV 为此提供了一个非常简单的接口。我们用相机捕捉一个视频(我用的电脑内置摄像头),将它转换成灰度视频并显示。仅仅是一个简单的开始。

去获取一个视频,你需要创建一个VideoCapture对象。它的参数可以是设备索引或者一个视频文件名。设备索引仅仅是摄像机编号。通常会连接一台摄像机(as in my case)。所以我只传了 0(或者-1)。你可以通过传 1 来选择第二个摄像机,以此类推。之后,你能逐帧捕获。但是最后,不要忘记释放这个 Capture 对象。

import numpy as np
import cv2 as cv
cap = cv.VideoCapture(0)
while(True):
    # 一帧一帧捕捉
    ret, frame = cap.read()
    # 我们对帧的操作在这里
    gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
    # 显示返回的每帧
    cv.imshow('frame',gray)
    if cv.waitKey(1) & 0xFF == ord('q'):
        break
# 当所有事完成,释放 VideoCapture 对象
cap.release()
cv.destroyAllWindows()

cap.read() 返回一个 bool 值(True/False)。如果加载成功,它会返回True。因此,你可以通过这个返回值判断视频是否结束。

有时,cap 可能没有初始化 capture。在这种情况下,此代码显示错误。你可以通过该方法 cap.isOpened() 检查它是否初始化。如果它是 True,那么是好的,否则用 cap.open() 打开在使用。

你也可以通过使用 cap.get(propId) 函数获取一些视频的特征,这里的 propld 是一个 0-18 的数字,每个数字代表视频的一个特征 (如果这个视频有),或者使用 cv.VideoCapture.get() 获取全部细节。它们中有些值可以使用 cap.set(propId, value) 修改。Value 就是你想要的新值

例如:我可以用 cap.get(cv.CAP_PROP_FRAME_WIDTH) 获得宽, cap.get(cv.CAP_PROP_FRAME_HEIGHT) 获得高。它返回的是 640x480,但是我想把它修改为 320x240。仅使用 ret = cap.set(cv.CAP_PROP_FRAME_WIDTH,320)ret = cap.set(cv.CAP_PROP_FRAME_HEIGHT,240)

Note

  • 如果给你报错了,确保用任意其他的相机程序 (如 Linux 下的 Cheese 程序) 可以正常工作

播放视频文件

它和从相机捕获一样,只需要用视频文件名更改相机索引。同时显示 frame,为 cv.waitKey() 使用合适的时间。如果它太小,视频将非常快,如果太大,视频将很慢 (嗯,这就是如何显示慢动作)。正常情况下,25 毫秒就可以了。

import numpy as np
import cv2 as cv
cap = cv.VideoCapture('vtest.avi')
while(cap.isOpened()):
    ret, frame = cap.read()
    gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
    cv.imshow('frame',gray)
    if cv.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
cv.destroyAllWindows()

Note

  • 确保 ffmpeg 和 gstreamer 安装合适的版本。有时,使用 Video Capture 是比较头痛的,主要是因为错误的安装 ffmpeg 或 gstreamer。

保存视频

我们捕获视频,逐帧处理然后保存下来。对于图像来说,是非常的简单,就用 cv.imwrite()。这里需要做更多的工作。

这次我们创建一个 VideoWriter 对象。我们应该指定输出文件的名字 (例如:output.avi)。然后我们应该指定 FourCC 码 (下一段有介绍)。然后应该传递每秒帧数和帧大小。最后一个是 isColor flag。如果是 True,编码器期望彩色帧,否则它适用于灰度帧。

FourCC 是用于指定视频解码器的 4 字节代码。这里 fourcc.org 是可用编码的列表。它取决于平台,下面编码就很好。

  • In Fedora: DIVX, XVID, MJPG, X264, WMV1, WMV2. (XVID 是最合适的. MJPG 结果比较大. X264 结果比较小)
  • In Windows: DIVX (还需要测试和添加跟多内容)
  • In OSX: MJPG (.mp4), DIVX (.avi), X264 (.mkv).

对于 MJPG, FourCC 的代码作为 cv.VideoWriter_fourcc('M','J','P','G')cv.VideoWriter_fourcc(*'MJPG') 传递。

下面的代码从相机捕获,在垂直方向翻转每一帧然后保存它。

import numpy as np
import cv2 as cv
cap = cv.VideoCapture(0)
# 声明编码器和创建 VideoWrite 对象
fourcc = cv.VideoWriter_fourcc(*'XVID')
out = cv.VideoWriter('output.avi',fourcc, 20.0, (640,480))
while(cap.isOpened()):
    ret, frame = cap.read()
    if ret==True:
        frame = cv.flip(frame,0)
        # 写入已经翻转好的帧
        out.write(frame)
        cv.imshow('frame',frame)
        if cv.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break
# 释放已经完成的工作
cap.release()
out.release()
cv.destroyAllWindows()

绘图功能

目标

Code

上面的这些函数,你能看到一些相同的参数:

  • img:你想画的图片
  • color:形状的颜色,如 BGR,它是一个元组,例如:蓝色(255,0,0)。对于灰度图,只需传一个标量值。
  • thickness: 线或圆等的厚度。如果传 -1 就是像圆的闭合图形,它将填充形状。_默认 thickness = 1_
  • lineType:线条类型,如 8 连接,抗锯齿线等。默认情况下,它是 8 连接。cv.LINE_AA 画出抗锯齿线,非常好看的曲线。

画线

去画一条线,你需要传递线条的开始和结束的坐标。我们将创建一个黑色图像,并在坐上角到右下角画一条蓝色的线

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
# 创建一个黑色的图像
img = np.zeros((512,512,3), np.uint8)
# 画一条 5px 宽的蓝色对角线
cv.line(img,(0,0),(511,511),(255,0,0),5)
plt.imshow(img)

image-20240623164700871

画矩形

画一个矩形,你需要矩形的左上角和右下角。这次我们将会在图像的右上角画一个绿色的矩形。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

img = np.zeros((512,512,3), np.uint8)
cv.rectangle(img,(384,0),(510,128),(0,255,0),3)

plt.imshow(img)

image-20240623165246741

画圆

画一个圆,你需要它的圆心和半径。我们将在上面绘制的矩形上画一个内圆。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

img = np.zeros((512,512,3), np.uint8)
cv.circle(img,(447,63), 63, (0,0,255), -1)

plt.imshow(img)

image-20240623165505787

画椭圆

画一个椭圆,你需要传好几个参数。一个参数是圆心位置 (x,y)。下个参数是轴的长度 (长轴长度,短轴长度)。角度是椭圆在你逆时针方向的旋转角度。startAngle 和 endAngle 表示从长轴顺时针方向测量的椭圆弧的起点和终点。如整圆就传 0 和 360。更多细节请看 cv.ellipse() 的文档。下面是在这个图像中间画的一个半椭圆例子。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

img = np.zeros((512,512,3), np.uint8)
cv.ellipse(img,(256,256),(100,50),0,0,180,255,-1)

plt.imshow(img)

image-20240623165733841

画多边形

画多边形,首先你需要顶点的做坐标。将这些点组成一个形状为 ROWSx1x2 的数组,ROWS 是顶点数,它应该是 int32 类型。这里我们绘制一个顶点是黄色的小多边形。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

img = np.zeros((512,512,3), np.uint8)
pts = np.array([[10,5],[20,30],[70,20],[50,10]], np.int32)
pts = pts.reshape((-1,1,2))
cv.polylines(img,[pts],True,(0,255,255))

plt.imshow(img)

image-20240623165850011

Note

  • 如果地三个是 False,你将获得所有点的折线,而不是一个闭合形状。
  • cv.polylines() 能画很多线条。只需创建你想绘制所有线条的列表,然后将其传给这个函数。所有线条都将单独绘制。绘制一组线条比调用 cv.line() 好很多,快很多。

给图像加文字

在图像上加文字,你需要指定以下内容。

  • 你想写的文字数据。
  • 你想写的位置坐标 (如 左下角开始)。
  • 字体类型 (支持的字体,查看 cv.putText() 文档)
  • 常规的如颜色,粗细,线型等。为了更好看,线型使用 lintType = cv.LINE_AA
cv.putText(img, text, pos, fontFace,fontScale,color[, thickness[, lineType[, bottomLeftOrigin]]]) → img
参数说明
img表示输入图像,允许单通道灰度图像或多通道彩色图像。
text表示添加的文本字符串。
pos表示文本字符串 左下角坐标 ,(x, y) 格式的元组。
fontFace表示字体类型。
fontScale表示字体缩放比例因子。
color表示绘制直线的颜色,(b,g,r) 格式的元组,或者表示灰度值的标量。
thickness表示绘制直线的粗细,默认值 1px,-1 表示内部填充。
lineType表示绘制直线的线性,默认为 LINE_8。
bottomLeftOrigin表示为可选参数,默认值 True 表示数据原点位于左下角,False 表示位于左上角。

fontFace 值说明

描述
cv.FONT_HERSHEY_SIMPLEX表示正常大小无衬线字体。
cv.FONT_HERSHEY_PLAIN表示小号无衬线字体。
cv.FONT_HERSHEY_DUPLEX表示正常大小无衬线字体,比FONT_HERSHEY_SIMPLEX更复杂。
cv.FONT_HERSHEY_COMPLEX表示正常大小有衬线字体。
cv.FONT_HERSHEY_TRIPLEX表示正常大小有衬线字体,比FONT_HERSHEY_COMPLEX更复杂。
cv.FONT_HERSHEY_COMPLEX_SMALL表示FONT_HERSHEY_COMPLEX的小译本。
cv.FONT_HERSHEY_SCRIPT_SIMPLEX表示手写风格字体。
cv.FONT_HERSHEY_SCRIPT_COMPLEX表示手写风格字体,比FONT_HERSHEY_SCRIPT_SIMPLEX更复杂。
cv.FONT_ITALIC表示相应字体的斜体字。

lineType 值说明

描述
cv.LINE_4表示 4 邻接线型。
cv.LINE_8表示 8 邻接线型。
cv.LINE_AA表示抗锯齿线型,图像更平滑。

我们将在图像上写一个白色的 OpenCV

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

img = np.zeros((512,512,3), np.uint8)
text = 'OpenCV'
cv.putText(img, text, (125, 450), cv.FONT_HERSHEY_SIMPLEX, 2, (255, 255, 255), 3 ,cv.LINE_AA,)

plt.imshow(img)

image-20240623171955185

注意

OpenCV 不支持显示中文字符,使用 cv.putText() 时添加的文本字符串不能包含中文字符(包括中文标点符号)!!!

获取文本属性 cv.getTextSize 函数

cv.getTextSize(text, fontFace, fontScale, thickness)
参数说明
text表示添加的文本字符串。
fontFace表示字体类型。
fontScale表示字体缩放比例因子。
thickness表示绘制直线的粗细,默认值 1px,-1 表示内部填充。

返回参数说明

返回参数说明
(fw,fh)表示文本区域大小的宽高。
bh表示字体基线baseline位置。

总结

  • 函数 cv.putText 的参数 thickness 不能为负数,负数报错;
  • 函数 cv.line 的参数 thickness 不能为负数,负数报错。

鼠标作为画笔

参考文章

作为调色板的跟踪栏

参考文章

参考文章

核心操作

图像基本操作

学习:

  • 访问像素值并修改它们
  • 访问像素属性
  • 设置感兴趣区域(ROI)
  • 拆分和合并图像

本节中的几乎所有操作都主要与 Numpy 有关而非 OpenCV。熟悉 Numpy 后才能使用 OpenCV 编写更好的优化后代码。

(由于大多数代码都是单行的,所以示例将在 Python 终端中显示)

访问和修改像素

先来理解图像与一般的矩阵或张量的不同之处(不考虑图像的格式,元数据等信息)。首先,一张图像有自己的属性:宽,高,通道数。其中宽和高是我们肉眼可见的属性,而通道数则是图像能呈现色彩的属性。我们都知道,光学三原色是红色,绿色和蓝色,这三种颜色的混合可以形成任意的颜色。常见的图像的像素通道也是对应的R,G,B三个通道,在OpenCV中,每个通道的取值范围为0~255。(注:还有RGBA,YCrCb,HSV等其他图像通道表示方法)。即,一般彩色图像读进内存之后是一个h w c的矩阵,其中h为图像高(相当于矩阵的行),w为图像宽(相当于矩阵列),c为通道数。

下面我们先加载一副彩色图像,更准确地说,是一副黄色图像,如图所示。

黄色为绿色和红色的混合。所以,该图像的所有像素值都应为R=255,G=255,B=0。

import cv2 as cv
import numpy as np
img = cv.imread('./image/ht.png')
#图像大小为128*128*3
h,w,c = img.shape
print(h,w,c)
# 128 128 3

从上面的代码中可以看到,您可以通过行和列坐标访问像素值。注意,对于常见的RGB 图像,OpenCV的imread函数返回的是一个蓝色(Blue)值、绿色(Green)值、红色(Red)值的数组,维度大小为3。而对于灰度图像,仅返回相应的灰度值。

>>> img[100,100]
#OpenCV的读取顺序为B,G,R,由于图像所有像素为黄色,因此,G=255,R=255
array([  0, 255, 255], dtype=uint8)

# 仅访问蓝色通道的像素
>>> blue = img[100,100,0]
>>> print(blue)
0

你也可以使用同样的方法来修改像素值。

>>> img[100,100] = [255,255,255]
>>> print(img[100,100])
[255 255 255]

警告

Numpy 是一个用于快速阵列计算的优化库。因此,简单地访问每个像素值并修改其值将非常缓慢,并不鼓励这样做。

注意 上述方法通常用于选择数组的某个区域,比如前 5 行和后 3 列。对于单个像素的访问,可以选择使用 Numpy 数组方法中的 array.item()array.itemset(),注意它们的返回值是一个标量。如果需要访问所有的 G、R、B 的值,则需要所有像素分别调用 array.item()

更好的访问和编辑像素的方法:

#访问 红色通道 的值
>>>img.item(10,10,2)
255

#修改 红色通道 的值
>>>img.itemset((10,10,2),100)
>>>img.item(10,10,2)
100

访问图像属性

图像属性包括行数,列数和通道数,图像数据类型,像素数等。

与一般的numpy.array一样,可以通过 img.shape 访问图像的形状。它返回一组由图像的行、列和通道组成的元组(如果图像是彩色的):

>>>print(img.shape)
(128,128,3)
注意 如果图像是灰度图像,则返回的元组仅包含行数和列数,因此它是检查加载的图像是灰度图还是彩色图的一种很好的方法。

通过 img.size 访问图像的总像素数:

>>>print(img.size)
562248

图像数据类型可以由 img.dtype 获得:

>>>print(img.dtype)
UINT8
注意 img.dtype 在调试时非常重要,因为 OpenCV-Python 代码中的大量错误由无效的数据类型引起。

图像中的感兴趣区域

有时您将不得不处理某些图像区域。对于图像中的眼部检测,在整个图像上进行第一次面部检测。当获得面部后,我们单独选择面部区域并在其内部搜索眼部而不是搜索整个原始图像。它提高了准确性(因为眼睛总是在脸上:D)和性能(因为我们在一个小区域搜索)。

使用 Numpy 索引再次获得 ROI(感兴趣区域)。在这里,我选择眼睛并将其复制到图像中的另一个区域:

import cv2 as cv
img = cv.imread('./01_Picture/07_Lena.jpg')
img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
eye = img[130:145,123:180]
img[35:50,93:150] = eye
plt.imshow(img)

image-20240922230441973

拆分和合并图像通道

有时您需要在 B,G,R 通道图像上单独工作。在这种情况下,您需要将 BGR 图像分割为单个通道。在其他情况下,您可能需要将这些单独的通道合并至 BGR 图像。您可以通过以下方式完成:

b,g,r = cv.split(img)
img = cv.merge((b,g,r))

或者使用numpy.array的切片方法

b = img[:,:,0]
g = img[:,:,1]
r = img[:,:,2]

假设您要将所有红色像素设置为零,则无需先拆分通道。Numpy 索引更快:

img[:,:,2] = 0

警告

CV.spilt()是一项代价高昂的操作(就时间而言)。所以只在你需要时再这样做,否则使用 Numpy 索引。

制作图像边界(填充)

如果要在图像周围创建边框,比如相框,可以使用 CV.copyMakeBorder()。但它有更多卷积运算,零填充等应用。该函数采用以下参数:

  • src 输入的图像
  • top,bottom,left,right 上下左右四个方向上的边界拓宽的值
  • borderType 定义要添加的边框类型的标志。它可以是以下类型:

  • value- 如果边框类型为CV.BORDER_CONSTANT,则这个值即为要设置的边框颜色

下面是一个示例代码,演示了上述所有边框类型,以便更好地理解:

import cv2 as cv
from matplotlib import pyplot as plt


# 导入图片
img = cv.imread('./01_Picture/01_cat.jpg')
img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
top_size,bottom_size,left_size,right_size = (50,50,50,50)  # 填充多少区域

# cv.copyMakeBorder(src, top, bottom, left, right, borderType, dst, value)
replicate = cv.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv.BORDER_REPLICATE) 
reflect = cv.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv.BORDER_REFLECT)
reflect_101 = cv.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv.BORDER_REFLECT_101)
wrap = cv.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv.BORDER_WRAP)
constant = cv.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv.BORDER_CONSTANT, value=[255,255,0])

plt.subplot(231),plt.imshow(img),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect_101),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant),plt.title('CONSTANT')
plt.show()

请参阅下面的结果。(图像是通过 matplotlib 展示的。因此互换了红色和蓝色通道):

image-20240922232147862