Skip to content

Opencv


环境配置地址:


一、图像基本操作


lena_img


如上图 :

  • 图片在我们肉眼五颜六色,各式各样的形状的,但是在计算机我们称为

    —— 就是一堆矩阵,然后矩阵其实就是一堆数字,这些数字就是

  • 像上面那样分成一小块,然后再细分,这样就变成一个一个像素点的形式,

    一个像素点又分了三个通道 , 也就是我们熟悉的 ,分别代表红色、

    绿色、蓝色 分别的像素值是多少,我们可以通过这 的不同程度的组合

    可以组合出各种颜色,像素值的取值范围是 , 其中 越接近 0 是越 暗,

    越 接近 255 是越 亮 ,所以如果不是三通道,如果是 的话,像素值 0

    就是对应黑色 , 像素值 255 就是对应 白色 。


微信截图_20250201214950


  • 色彩三原色(CMYK):品红、黄、青

  • 光学三原色(RGB):红、绿、蓝



数据读取-图像


使用到的 cat.jpg :


cat
python
import cv2 			# !!!opencv读取的格式是 BGR 不是RGB!!!
import matplotlib.pyplot as plt
import numpy as np 
%matplotlib inline

img=cv2.imread('cat.jpg')
python
img

微信截图_20250201220434

可以看出:读进去的图像就是一堆矩阵的形式


  • 图像的显示

显示图像有两种方式:

  • 使用 cv2 会弹出一个窗口的形式来展示图像
  • 使用 plt 可以在 jupyter 浏览器中渲染出来,会在单元格输出里

使用 plt 展示图像之前还有一个很重要的点:


微信截图_20250201220753


python
# 因为前面 img 是用cv2读进来的,所以它是BGR的形式
# 但是plt是RGB的,所以先要转换成 RGB
img2 = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
plt.imshow(img2)

微信截图_20250201221124

使用 cv2 读的 img 就可以直接用 cv2 显示了,但是注意要有额外控制窗口的代码

python
# 图像的显示,也可以创建多个窗口
cv2.imshow('image',img) 
# 等待时间,毫秒级,0表示任意键终止
# 其他数字 x , 代表 x 毫秒后关闭
cv2.waitKey(0)
# 一定要搭配这个使用,前一句代码本质是卡住程序而已,这个才是销毁窗口
cv2.destroyAllWindows()

这一大串这么麻烦, 难道每次都要重新写这么多么,我们可以自定义一个函数 cv_show()

python
def cv_show(name,img):
    """
    name: 窗口名字
    img: 图像array
    """
    cv2.imshow(name,img) 
    cv2.waitKey(0) 
    cv2.destroyAllWindows()

  • 图像 array 的 形状
python
img.shape

# 输出: (414, 500, 3)
# (h 横向像素点数,w 纵向像素点数,c 通道数)

  • 以 灰度图 的形式读图
python
img=cv2.imread('cat.jpg',cv2.IMREAD_GRAYSCALE)
img
python
# 调用前面自定义的图像显示函数

cv_show("gray_img",img)

微信截图_20250201222414
  • 图像保存
python
#保存
cv2.imwrite('mycat.png',img)

# 输出: True

  • 数字图像特征
python
type(img)

# 输出:numpy.ndarray
python
img.size

# 输出:207000
# 其实就是 414 * 500 那个矩阵的大小
python
img.dtype

# 输出:dtype('uint8')

uint8 无符号8位整数类型 , 思考一下可以发现,8位存二进制可以表示十进制 0-255 ,也就是像素值范围



数据读取-视频


  • cv2.VideoCapture 可以捕获摄像头,用数字来控制不同的设备,例如 0 , 1
  • 如果是视频文件,直接指定好路径即可。

python
# 读取视频流
vc = cv2.VideoCapture('test.mp4')	# 随便找一个视频

# 如果找不到视频,并且是笔记本的话,可以读摄像头设备
#vc = cv2.VideoCapture(0)
python
# 检查是否打开正确
if vc.isOpened(): 
    open, frame = vc.read()
else:
    open = False
    
open
# 输出: True
python
# 写一个循环,一帧一帧的读视频
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(15) & 0xFF == 27:	# Esc 键,其键码为 27, 按Esc退出
            break
vc.release()
cv2.destroyAllWindows()


ROI 区域


ROI ( region of interest ) 即 是指在数据集中为特定目的而确定的样本。

例如,在医学影像学中,可以在图像或体积上定义肿瘤的边界,以便测量其大小。

可以在图像上定义心内膜边界,例如在心脏收缩期和舒张期等不同心脏周期阶段,以评估心脏功能。

在地理信息系统(GIS)中,ROI可以被理解为从2D地图中选择的多边形区域。

在计算机视觉和光学字符识别中,ROI定义了所考虑对象的边界。

在许多应用中,ROI 上会添加符号(文本)标签,以简洁地描述其内容。

在 ROI 内可能存在个别感兴趣点(POIs)。


  • 截取部分图像数据
python
img=cv2.imread('cat.jpg')
cat=img[0:50,0:200] 	# img本质是ndarray数组,那我们就可以使用切片来截取我们想要的区域
cv_show('cat',cat)		# 使用前面自定义的cv_show来显示图像

  • 颜色通道的提取
python
b,g,r=cv2.split(img)	# 记得cv2是BGR通道
python
r
"""
array([[160, 164, 169, ..., 185, 184, 183],
       [126, 131, 136, ..., 184, 183, 182],
       [127, 131, 137, ..., 183, 182, 181],
       ...,
       [198, 193, 178, ..., 206, 195, 174],
       [176, 183, 175, ..., 188, 144, 125],
       [190, 190, 157, ..., 200, 145, 144]], dtype=uint8)
"""
python
r.shape

# (414, 500)
python
img=cv2.merge((b,g,r))		# 再合并起来三个通道
img.shape

# (414, 500, 3)
python
# 只保留R , 查看只有红色的图像
cur_img = img.copy()
cur_img[:,:,0] = 0		# B通道置0
cur_img[:,:,1] = 0		# G通道置0
cv_show('R',cur_img)

微信截图_20250205034111
python
# 只保留G
cur_img = img.copy()
cur_img[:,:,0] = 0
cur_img[:,:,2] = 0
cv_show('G',cur_img)
python
# 只保留B
cur_img = img.copy()
cur_img[:,:,1] = 0
cur_img[:,:,2] = 0
cv_show('B',cur_img)


边界填充


边界填充涉及在图像边界添加额外的像素,以便在进行图像处理操作(如卷积)时,避免边界信息的丢失。

OpenCV中的 cv2.copyMakeBorder() 函数可以用来实现边界填充,它允许用户指定边界的宽度和填充类型


python
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)
python
import matplotlib.pyplot as plt
plt.subplot(231), plt.imshow(img, 'gray'), plt.title('ORIGINAL')
plt.subplot(232), plt.imshow(replicate, 'gray'), plt.title('REPLICATE')
plt.subplot(233), plt.imshow(reflect, 'gray'), plt.title('REFLECT')
plt.subplot(234), plt.imshow(reflect101, 'gray'), plt.title('REFLECT_101')
plt.subplot(235), plt.imshow(wrap, 'gray'), plt.title('WRAP')
plt.subplot(236), plt.imshow(constant, 'gray'), plt.title('CONSTANT')

plt.show()

微信截图_20250205034937
  • BORDER_REPLICATE :复制法,也就是复制最边缘像素。

  • BORDER_REFLECT :反射法,对感兴趣的图像中的像素在两边进行复制

    例如:fedcba|abcdefgh|hgfedcb

  • BORDER_REFLECT_101 :反射法,也就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba

  • BORDER_WRAP:外包装法 , cdefgh|abcdefgh|abcdefg

  • BORDER_CONSTANT :常量法,常数值 0 or 255 ( 纯黑/白 ) 填充。


数值计算


使用到的 dog.jpg


dog
python
# 读图,记得cv2读图是BGR
img_cat=cv2.imread('cat.jpg')
img_dog=cv2.imread('dog.jpg')
python
# 因为本质是 ndarray ,可以做一些数值计算

img_cat2= img_cat +10 
img_cat[:5,:,0]

"""
array([[142, 146, 151, ..., 156, 155, 154],
       [108, 112, 118, ..., 155, 154, 153],
       [108, 110, 118, ..., 156, 155, 154],
       [139, 141, 148, ..., 156, 155, 154],
       [153, 156, 163, ..., 160, 159, 158]], dtype=uint8)

"""
python
img_cat2[:5,:,0]

"""
array([[152, 156, 161, ..., 166, 165, 164],
       [118, 122, 128, ..., 165, 164, 163],
       [118, 120, 128, ..., 166, 165, 164],
       [149, 151, 158, ..., 166, 165, 164],
       [163, 166, 173, ..., 170, 169, 168]], dtype=uint8)
"""
python
# 相当于% 256
# 因为这个数据结构本质是uint8,数据范围0-255,超出就会从0算起
(img_cat + img_cat2)[:5,:,0] 

"""
array([[ 38,  46,  56, ...,  66,  64,  62],
       [226, 234, 246, ...,  64,  62,  60],
       [226, 230, 246, ...,  66,  64,  62],
       [ 32,  36,  50, ...,  66,  64,  62],
       [ 60,  66,  80, ...,  74,  72,  70]], dtype=uint8)
"""

  • 图像融合
python
# 两张图片的size不一样,如果执行这个代码就会报错:
# ValueError: operands could not be broadcast together with shapes (414,500,3) (429,499,3) 

# img_cat + img_dog

需要先改变成一样的尺寸,可以使用 cv2.resize()

python
img_cat.shape

# (414, 500, 3)
python
img_dog = cv2.resize(img_dog, (500, 414))	# 这里传入的参数是 (w,h)别搞反了
img_dog.shape

# (414, 500, 3)

变成一样的尺寸了就可以做图像融合,使用 cv2.addWeighted()

其实函数名就很直观了,其实就是做了一种 的感觉

python
# cv2.addWeighted(src1,alpha,src2,beta,gamma)
# R = alpha * src1 + beta * src2 + gamma
res = cv2.addWeighted(img_cat, 0.4, img_dog, 0.6, 0)

微信截图_20250206030408

做完图像融合就像这样,又可以看到猫又可以看到狗,(怎么感觉有点像网上说的 “宿命感“~)


改变图像大小还可以是传入 (0,0) 去尺寸部分,然后后面传入 横尺寸称几倍,纵尺寸乘几倍

python
res = cv2.resize(img, (0, 0), fx=4, fy=4)
plt.imshow(res)
python
res = cv2.resize(img, (0, 0), fx=1, fy=3)
plt.imshow(res)

图像色彩不对,其实本质还是那句话,cv2BGRpltRGB ,这里就不换了,正好展示一下效果


微信截图_20250206030807


二、阈值与平滑处理


图像阈值


ret, dst = cv2.threshold(src, thresh, maxval, type)

  • src: 输入图,只能输入单通道图像,通常来说为灰度图
  • dst: 输出图
  • thresh: 阈值
  • maxval: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
  • type:二值化操作的类型,包含以下5种类型: cv2.THRESH_BINARY; cv2.THRESH_BINARY_INV; cv2.THRESH_TRUNC; cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV
  • cv2.THRESH_BINARY 超过阈值部分取 maxval(最大值),否则取0
  • cv2.THRESH_BINARY_INV THRESH_BINARY的反转
  • cv2.THRESH_TRUNC 大于阈值部分设为阈值,否则不变
  • cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0
  • cv2.THRESH_TOZERO_INV THRESH_TOZERO的反转
python
img=cv2.imread('cat.jpg')
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
python
ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV)

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

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

输入的 plt 如下 :

tuxiangyuzhi