上一期【人工智能-11】OpenCV基础认知和使用

下一期【人工智能-13】OpenCV插值方法,边缘填充,图像矫正,图像掩膜,图像融合与噪点消除

一、色彩空间及转换

色彩空间转换就是把图像所在的色彩空间换到另一个色彩空间,这是一个非常基础且重要的操作,可以更加方便的对图像进行处理。

1.色彩空间

常见的色彩空间有RGB,HSV,YUV等,色彩空间是通过几种色彩指标相互组合所构成的一个空间,包含几乎所有的颜色。
RGB色彩空间 :通过红(R)绿(G)蓝(B)三种颜色的不同强度的光来组合创建其他颜色,可以使用RGB空间生成任意一种颜色。在OpenCV中,是以BGR方式存储
HSV色彩空间:使用色调(Hue)、饱和度(Saturation)和亮度(Value)三个参数来表示颜色,色调H表示颜色的种类,如红色、绿色、蓝色等;饱和度表示颜色的纯度或强度,如红色越纯,饱和度就越高;亮度表示颜色的明暗程度,如黑色比白色亮度低。
为什么要把RGB转换成HSV

  • 符合人类的感知方式,人类对颜色的感知其实就是基于HSV方式的。
  • 颜色调整更直观,在RGB空间中要调整红色系的颜色,需要同时调整R、G、B三个通道的数值,而在HSV空间中只需要调整色调和饱和度即可。
  • 降维处理有利于计算,降维处理可以减少计算的复杂性和计算量。HSV颜色空间相对于RGB颜色空间,减少了两个维度。这有利于图像处理。

2.颜色加法

在OpenCV中图像的表现形式都是数组,也就是说可以通过数组相加对颜色进行改变。

import cv2 as cv
import numpy as np
# 读取图片
cao = cv.imread('../images/cao.png')
pig = cv.imread('../images/pig.png')

# 饱和操作 cv.add(img1, img2),相加的条件是img1和img2的形状一致
dst1 = cv.add(cao, pig)
# print(cao)  # [ 55  90  48]
# print(pig)  # [0 0 0]
# print(dst1)  # [ 55  90  48]
# numpy直接相加,取模运算,对256进行取模运算,250+10=260%256=4
# 不推荐使用,会造成颜色失真
cv.imshow('dst1', dst1)
# 例子
x = np.uint8([[250]])
y = np.uint8([[10]])
xy1 = cv.add(x, y)
xy2 = x + y  # 取模运算
print(xy1)  # [[255]]
print(xy2)  # [[4]]
# 推荐使用
# 颜色加权加法 cv.addWeighted(img1, alpha, img2, beta, gamma)
# alpha:img1的权重,beta:img2的权重,gamma:亮度调整值 >0变亮 <0变暗 =0不变
dst2 = cv.addWeighted(cao, 0.3, pig, 0.7, 50)
cv.imshow('dst2', dst2)

cv.waitKey(0)
cv.destroyAllWindows()

3.颜色转换

cv2.cvtColor是OpenCV中的一个函数,用于图像颜色空间的转换。可以将一个图像从一个颜色空间转换为另一个颜色空间,比如从RGB到灰度图,或者从RGB到HSV的转换等。

# 读取图片
img = cv.imread('../images/cat1.png')
# 颜色转换 cv.cvtColor(img, cv.COLOR_BGR2HSV)->图像,转换方式
# 灰度图
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
cv.imshow('gray',gray)
# 转hsv
hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)
cv.imshow('hsv',hsv)
# bgr转rgb
rgb = cv.cvtColor(img,cv.COLOR_BGR2RGB)
cv.imshow('rgb',rgb)

cv.waitKey(0)
cv.destroyAllWindows()

二、灰度化

灰度图:每个像素只有一个采样颜色的图像,这类图像通常显示为从最暗黑色到最亮的白色的灰度

1.最大值法

如果图片是三通道图片,获取三通道中最大的一个颜色值。

#读取图像
pig = cv.imread('../images/pig.png')
shape = pig.shape  # (h,w,c)
img = np.zeros((shape[0], shape[1]), dtype=np.uint8)
# 循环遍历每一行 img[0,0,0]
for i in range(shape[0]):
    for j in range(shape[1]):
        # 使用max()函数获取每个像素点的最大值
        img[i,j] = max(pig[i, j, 0],pig[i, j, 1],pig[i, j, 2])

2.平均值法

将三通道中每个颜色值相加再整除3,得到的平均数。注意里面的类型是uint8,记得转换成int后再运算,之后再转换成uint8(这样符合规范)

# 读取图片
pig = cv.imread('../images/pig.png')
shape = pig.shape  # (h,w,c)
img = np.zeros((shape[0], shape[1]), dtype=np.uint8)
# 循环遍历每一行 img[0,0,0]
for i in range(shape[0]):
    for j in range(shape[1]):
        # 使用int()转换为更大的数据类型,防止溢出,再使用np.uint8()转换为8位无符号整型
        img[i,j] = np.uint8((int(pig[i, j, 0])+int(pig[i, j, 1])+int(pig[i, j, 2]))//3)

3.加权均值

对于彩色图像的每个像素,它会按照一定的权重去乘以每个通道的像素值,并将其相加,得到最后的值就是灰度图像中对应位置的像素值

pig = cv.imread('../images/pig.png')
shape = pig.shape  # (h,w,c)
img = np.zeros((shape[0], shape[1]), dtype=np.uint8)
# 定义权重
wb, wg, wr = 0.114, 0.587, 0.299  # 这个权重其实就是通过API直接将图像读取为灰度图的权重
# 循环遍历每一行 img[0,0,0]
for i in range(shape[0]):
    for j in range(shape[1]):
        # 按照一定的权重去乘以每个通道的像素值,并将其相加,得到最后的值就是灰度图像中对应位置的像素值,round四舍五入
        img[i,j] = round(wb*pig[i, j, 0]+wg*pig[i, j, 1]+wr*pig[i, j, 2])

三、二值化

二值化就是将图像的所有像素改为0或者255,二值图像通常用于文字、线条图的扫描识别(OCR)和掩膜图像的存储。注意:进行二值化的图像必须要是灰度图

1.阈值法(THRESH_BINARY)

阈值法就是通过设置一个阈值,将灰度图中的每一个像素值与该阈值进行比较,小于等于阈值的像素就被设置为0(通常代表背景),大于阈值的像素就被设置为maxval(通常代表前景).
全局阈值法:即是对全图的设置,这次除了自适应二值化都为全局。

_,binary = cv2.threshold(img,thresh,maxval,type)
# 将type设为cv.THRESH_BINARY就是阈值法
  • img:输入图像,要进行二值化处理的灰度图。
  • thresh:设定的阈值。当像素值大于(或小于,取决于阈值类型)thresh时,该像素被赋予的值。
  • type:阈值处理的类型。
  • 返回值:
    • 第一个值(通常用下划线表示):计算出的阈值,若使用自适应阈值法,会根据算法自动计算出这个值。
    • 第二个值(binary):二值化后的图像矩阵。与输入图像尺寸相同。
# 二值化,阈值法,cv.threshold(输入图片,阈值,最大值(一般是255),阈值类型)
# 使用_忽略返回值(127)
_,binary = cv.threshold(gray,127,255,cv.THRESH_BINARY)
# 小于等于127设为0,大于设为255

2.反阈值法(THRESH_BINARY_INV)

反阈值法其实就是阈值法反过来了,大于的设为0,小于等于设为255

# 反阈值法,其实就是阈值类型用的是cv.THRESH_BINARY,只是将阈值位置取反
_,binary_inv = cv.threshold(gray,127,255,cv.THRESH_BINARY_INV)

3. 截断阈值法(THRESH_TRUNC)

截断阈值法,当像素超过阈值时,将其改为阈值

# 截断阈值法,阈值类型用cv.THRESH_TRUNC,会将大于阈值的像素值设置为阈值
_,binary_trunc = cv.threshold(gray,170,255,cv.THRESH_TRUNC)

4.零处理

设定一个阈值,将超过或着低于的像素值改为0

# 低阈值零处理,阈值类型用cv.THRESH_TOZERO,会将小于阈值的像素值设置为0
_,binary_zero = cv.threshold(gray,127,255,cv.THRESH_TOZERO)
cv.imshow('binary_zero',binary_zero)
# 超阈值零处理,阈值类型用cv.THRESH_TOZERO_INV,会将大于阈值的像素值设置为0
_,binary_zero_inv = cv.threshold(gray,127,255,cv.THRESH_TOZERO_INV)
cv.imshow('binary_zero_inv',binary_zero_inv)

5.OTSU阈值法

OTSU阈值法是用来计算阈值的,它想要使用要和阈值法或者反阈值法相结合。

# OTSU阈值法,阈值类型用cv.THRESH_OTSU,会自动计算阈值.需要和阈值法(默认)或反阈值法一起使用
thresh1,otsu = cv.threshold(gray,200,255,cv.THRESH_OTSU)
# 这里自己写上去的阈值是无效的

6.自适应二值化

自适应二值化更加适合用在明暗分布不均的图片,因为图片的明暗不均,导致图片上的每一小部分都要使用不同的阈值进行二值化处理,这时候传统的二值化算法就无法满足我们的需求了,于是就出现了自适应二值化。

cv2.adaptiveThreshold(image_np_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 7, 10)

maxval:最大阈值,一般为255

adaptiveMethod:小区域阈值的计算方式:

ADAPTIVE_THRESH_MEAN_C:小区域内取均值

ADAPTIVE_THRESH_GAUSSIAN_C:小区域内加权求和,权重是个高斯核

thresholdType:二值化方法,只能使用THRESH_BINARY、THRESH_BINARY_INV,也就是阈值法和反阈值法

blockSize:选取的小区域的面积,如7就是7*7的小块。

c:最终阈值等于小区域计算出的阈值再减去此值

# 自适应二值化 是一种基于图像局部的二值化方法 必须结合阈值法或反阈值法一起使用
# 自适应二值化,取均值 cv.adaptiveThreshold(输入图片,最大值,阈值类型, adaptive_method, block_size(必须是奇数), C)
# c:最终阈值等于小区域计算出的阈值再减去此值
adaptive_thresh = cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY,7,2)
cv.imshow('adaptive_thresh',adaptive_thresh)
# 自适应二值化,加权求和 cv.adaptiveThreshold(输入图片,最大值,阈值类型, adaptive_method, block_size, C)
adaptive_thresh_gaussian = cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY,7,2)
cv.imshow('adaptive_thresh_gaussian',adaptive_thresh_gaussian)

四、图像翻转

在OpenCV中,图片的镜像旋转是以图像的中心为原点进行镜像翻转的。

  • cv2.flip(img,flipcode)

  • 参数

    • im 要翻转的图像
    • flipcode: 指定翻转类型的标志
      • flipcode=0: 垂直翻转,图片像素点沿x轴翻转
      • flipcode>0: 水平翻转,图片像素点沿y轴翻转
      • flipcode<0: 水平垂直翻转,水平翻转和垂直翻转的结合
face = cv.imread("../images/face.png")
cv.imshow("old", face)
# 翻转(也叫镜像旋转)图片,以图像中心为原点 cv.flip(img, flipcode)->图像,翻转方式
# flipcode>0:水平翻转,图片像素点沿y轴翻转
# <0:水平垂直翻转,水平翻转和垂直翻转的结合
# =0:垂直翻转,图片像素点沿x轴翻转
flip_0 = cv.flip(face, 0)
cv.imshow("flip_0", flip_0)
cv.waitKey(0)
cv.destroyAllWindows()

五、仿射变换

仿射变换(Affine Transformation)是一种线性变换,保持了点之间的相对距离不变。

  • 仿射变换的基本性质

    • 保持直线
    • 保持平行
    • 比例不变性
    • 不保持角度和长度
  • 常见的仿射变换类型

    • 旋转:绕着某个点或轴旋转一定角度。
    • 平移:仅改变物体的位置,不改变其形状和大小。
    • 缩放:改变物体的大小。
    • 剪切:使物体发生倾斜变形。
      基本原理
import cv2 as cv
import numpy as np
# 旋转
# 读取图片
cat = cv.imread('../images/cat1.png')
cat = cv.resize(cat, (600, 600))
# 获取旋转矩阵 cv.getRotationMatrix2D(旋转中心,旋转角度(逆时针为正方向),缩放比例) 2x3
M = cv.getRotationMatrix2D((300,300), -45, 1)
# 仿射变换 cv.warpAffine(img, 旋转矩阵M, 输出图像尺寸(width, height))
# 图像输出尺寸如果超过原图尺寸,则超出部分用黑色填充,如果缩小,则原图超出输出部分不显示
rotate = cv.warpAffine(cat, M, (600, 600))
cv.imshow('rotate', rotate)
# 平移
# 定义平移量
tx = 120
ty = 120
# 定义平移矩阵
M = np.float32([[1,0,tx],[0,1,ty]])
translate = cv.warpAffine(cat, M, (600, 600))
cv.imshow('translate', translate)
# 缩放
# 缩放比例
sx = 0.6
sy = 0.5
# 定义缩放矩阵
M = np.float32([[sx,0,tx],[0,sy,ty]])
scale = cv.warpAffine(cat, M, (600, 600))
cv.imshow('scale', scale)

# 剪切
# 定义剪切系数(用的乘法)
x1 = 0.1
y1 = 0
# 定义剪切矩阵
M = np.float32([[1,y1,0],[x1,1,0]])
cut = cv.warpAffine(cat, M, (600, 600))
cv.imshow('cut', cut)

cv.waitKey(0)
cv.destroyAllWindows()

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐