文章目录
参考
系列文章
- 【python】OpenCV—RGB, Rectangle, Circle, SS(1)
- 【python】OpenCV—Blur, Threshold, Gradient, Morphology(2)
- 【python】OpenCV—Edge, Corner, Face Detection(3)
辅助手册 OpenCV 帮助文档
安装
OpenCV
是 Open Source Computer Vision
的缩写,由英特尔公司于1999年推出。它最初是用 C/ C++
编写的,所以你可能会看到更多用 C 语言而不是 Python 编写的教程。但现在它在 Python 中也被广泛用于计算机视觉。
在安装 anaconda 的情况下,conda install opencv
即可!参考 Ubuntu16.04.3服务器上opencv和caffe的配置
安装指定版本提示找不到的话,可以试试先 search
conda search opencv
conda install opencv=XXX
pip 的话则
pip install opencv-python
0 基础操作
获取像素值,修改像素值
import cv2
img = cv2.imread("RandomColor.jpg") # (100, 400, 3)
print(img.item(0, 0, 0))
img.itemset((0, 0, 0), 255)
print(img.item(0, 0, 0))
output
145
255
查看图像属性
import cv2
img = cv2.imread("1.jpg")
print("shape:", img.shape)
print("size:", img.size)
print("dtype:", img.dtype) # 图像的数据类型
out
shape: (1200, 1920, 3)
size: 6912000
dtype: uint8
1 RGB / HSV / HSL / YUV
1.1 RGB
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
# Import the image
img = cv2.imread('C://Users/13663//Desktop/1.jpg')
plt.axis("off")
plt.imshow(img)
颜色怪怪的,这是因为 OpenCV 中颜色模式的默认设置顺序是 BGR
,不同与 Matplotlib。因此,要在 RGB 模式下查看图像,我们需要将它从 BGR 转换为 RGB(为什么会多出来黑框框,我偷懒没有保存图片,notebook 的主题是黑色的,直接截的图),如下所示
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.axis("off")
plt.imshow(img_rgb)
Ps:当然,opencv 读出来的数据是 numpy.ndarray
形式,我们也可以用如下的形式把 BGR 改成 RGB
plt.imshow(img_rgb[:,:,::-1])
也即 h 和 w 维度不变,c 维度逆一下
转成灰度图看看
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.axis("off")
plt.imshow(img_gray, cmap = 'gray')
看看 rgb 模式中每个通道的情况!
fig, axs = plt.subplots(nrows = 1, ncols = 3,figsize = (20, 20))
for i in range(0, 3):
ax =axs[i]
ax.imshow(img_rgb[:, :, i], cmap = 'gray')
ax.axis("off")
plt.show()
下图依次为 r,g,b
可以看出,虽然都是灰度,但是还是有明显差别!在 r 通道中,越红越接近255,表现出来越白,其它通道同理!
我用 Photo Shop 进一步验证了这个结论
首先在原图上画 r、g、b 的矩形框,绘画方法参考 看懂 RGB 彩色通道
然后在不同的通道下观察这三种纯色矩形框的颜色!
1)r 通道下
纯红色为255,也即白色,蓝绿都是0,黑色
2)g 通道下
纯绿色为255,也即白色,红蓝都是0,黑色
3)b 通道下
纯蓝色为255,也即白色,红绿都是0,黑色
4)g+b 通道下
蓝绿不忘初心,没有红,0,黑色!
5)r+b 通道下
红蓝不忘初心,没有绿,0,黑色!
补充:通道置为 0
import cv2
img = cv2.imread("1.jpg")
img[:,:,1] = 0
cv2.imwrite("2.jpg", img)
6)r+g 通道下
红绿不忘初心,没有蓝,0,黑色!
1.2 HSV
HSV 等于 HSB B 是 brightness
HSV 是一种比较直观的颜色模型,所以在许多图像编辑工具中应用比较广泛,这个模型中颜色的参数分别是:
- 色调(H, Hue): 用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,品红为300°;
- 饱和度(S,Saturation): 表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。
- 明度(V, Value):明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。
Saturation:0-100
Brightness:0-100
Hue:0-360
PS 中上图横坐标 Saturation,纵坐标 Brightness,右上角最纯
色带是Hue色调,0 代表红色,120 代表绿色,240 代表蓝色。我们可以自定义 0-355 这 360 个数值,实现不同的色调转换。
# Transform the image into HSV
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Plot the converted images
plt.axis("off")
plt.imshow(img_hsv)
下面来个改变图片颜色的应用
import colorsys
from PIL import Image
# 输入文件
filename = 'C://Users/Administrator/Desktop/1.jpg'
# 目标色值
target_hue = 0
# 读入图片,转化为 RGB 色值
image = Image.open(filename).convert('RGBA')
# 将 RGB 色值分离
image.load()
r, g, b, a = image.split()
result_r, result_g, result_b, result_a = [], [], [], []
# 依次对每个像素点进行处理
for pixel_r, pixel_g, pixel_b, pixel_a in zip(r.getdata(), g.getdata(), b.getdata(), a.getdata()):
# 转为 HSV 色值
h, s, v = colorsys.rgb_to_hsv(pixel_r / 255., pixel_b / 255., pixel_g / 255.)
# 转回 RGB 色系
rgb = colorsys.hsv_to_rgb(target_hue/360, s, v)
pixel_r, pixel_g, pixel_b = [int(x * 255.) for x in rgb]
# 每个像素点结果保存
result_r.append(pixel_r)
result_g.append(pixel_g)
result_b.append(pixel_b)
result_a.append(pixel_a)
r.putdata(result_r)
g.putdata(result_g)
b.putdata(result_b)
a.putdata(result_a)
# 合并图片
image = Image.merge('RGBA', (r, g, b, a))
# 输出图片
image.save('output.png')
原图
hue=0,s 和 b 和原图一样
target_hue = 120
target_hue = 240
1.3 HSL
和 HSV 字面上是一个意思!lightness 亮度!
在原理和表现上,HSB 和 HSV 中的 H(色相) 完全一致,但二者的 S(饱和度)不一样, L 和 V (明度 )也不一样
(色彩空间中的HSL、HSV、HSB有什么区别? - Forrest的回答 - 知乎 https://www.zhihu.com/question/22077462/answer/280114578):
- HSB 中的 S 控制纯色中混入白色的量,值越大,白色越少,颜色越纯;
- HSB 中的 B 控制纯色中混入黑色的量,值越大,黑色越少,明度越高;
- HSL 中的 S 和黑白没有关系,饱和度不控制颜色中混入黑白的多寡;
- HSL 中的 L 控制纯色中的混入的黑白两种颜色。
HSL中的 S,它用0%至100%的值描述了相同色相、明度下色彩纯度的变化。数值越大,颜色中的灰色越少,颜色越鲜艳,呈现一种从理性(灰度)到感性(纯色)的变化。
两者对比
更直观一点
# Transform the image into HLS models
img_hls = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)
# Plot the converted images
plt.axis("off")
plt.imshow(img_hls)
HSL
HSV(或者HSB)
HSB 的空间更贴近我的认知,但是这个 demo 还是 HSL 效果更好!
1.4 YUV
来自 YUV转RGB
import os
import cv2
import numpy as np
IMG_WIDTH = 1152
IMG_HEIGHT = 648
IMG_SIZE = int(IMG_WIDTH * IMG_HEIGHT * 3 / 2)
Y_WIDTH = IMG_WIDTH
Y_HEIGHT = IMG_HEIGHT
Y_SIZE = int(Y_WIDTH * Y_HEIGHT)
U_V_WIDTH = int(IMG_WIDTH / 2)
U_V_HEIGHT = int(IMG_HEIGHT / 2)
U_V_SIZE = int(U_V_WIDTH * U_V_HEIGHT)
def from_I420(yuv_data, frames):
Y = np.zeros((frames, IMG_HEIGHT, IMG_WIDTH), dtype=np.uint8)
U = np.zeros((frames, U_V_HEIGHT, U_V_WIDTH), dtype=np.uint8)
V = np.zeros((frames, U_V_HEIGHT, U_V_WIDTH), dtype=np.uint8)
for frame_idx in range(0, frames):
y_start = frame_idx * IMG_SIZE
u_start = y_start + Y_SIZE
v_start = u_start + U_V_SIZE
v_end = v_start + U_V_SIZE
Y[frame_idx, :, :] = yuv_data[y_start : u_start].reshape((Y_HEIGHT, Y_WIDTH))
U[frame_idx, :, :] = yuv_data[u_start : v_start].reshape((U_V_HEIGHT, U_V_WIDTH))
V[frame_idx, :, :] = yuv_data[v_start : v_end].reshape((U_V_HEIGHT, U_V_WIDTH))
return Y, U, V
def from_YV12(yuv_data, frames):
Y = np.zeros((frames, IMG_HEIGHT, IMG_WIDTH), dtype=np.uint8)
U = np.zeros((frames, U_V_HEIGHT, U_V_WIDTH), dtype=np.uint8)
V = np.zeros((frames, U_V_HEIGHT, U_V_WIDTH), dtype=np.uint8)
for frame_idx in range(0, frames):
y_start = frame_idx * IMG_SIZE
v_start = y_start + Y_SIZE
u_start = v_start + U_V_SIZE
u_end = u_start + U_V_SIZE
Y[frame_idx, :, :] = yuv_data[y_start : v_start].reshape((Y_HEIGHT, Y_WIDTH))
V[frame_idx, :, :] = yuv_data[v_start : u_start].reshape((U_V_HEIGHT, U_V_WIDTH))
U[frame_idx, :, :] = yuv_data[u_start : u_end].reshape((U_V_HEIGHT, U_V_WIDTH))
return Y, U, V
def from_NV12(yuv_data, frames):
Y = np.zeros((frames, IMG_HEIGHT, IMG_WIDTH), dtype=np.uint8)
U = np.zeros((frames, U_V_HEIGHT, U_V_WIDTH), dtype=np.uint8)
V = np.zeros((frames, U_V_HEIGHT, U_V_WIDTH), dtype=np.uint8)
for frame_idx in range(0, frames):
y_start = frame_idx * IMG_SIZE
u_v_start = y_start + Y_SIZE
u_v_end = u_v_start + (U_V_SIZE * 2)
Y[frame_idx, :, :] = yuv_data[y_start : u_v_start].reshape((Y_HEIGHT, Y_WIDTH))
U_V = yuv_data[u_v_start : u_v_end].reshape((U_V_SIZE, 2))
U[frame_idx, :, :] = U_V[:, 0].reshape((U_V_HEIGHT, U_V_WIDTH))
V[frame_idx, :, :] = U_V[:, 1].reshape((U_V_HEIGHT, U_V_WIDTH))
return Y, U, V
def from_NV21(yuv_data, frames):
Y = np.zeros((frames, IMG_HEIGHT, IMG_WIDTH), dtype=np.uint8)
U = np.zeros((frames, U_V_HEIGHT, U_V_WIDTH), dtype=np.uint8)
V = np.zeros((frames, U_V_HEIGHT, U_V_WIDTH), dtype=np.uint8)
for frame_idx in range(0, frames):
y_start = frame_idx * IMG_SIZE
u_v_start = y_start + Y_SIZE
u_v_end = u_v_start + (U_V_SIZE * 2)
Y[frame_idx, :, :] = yuv_data[y_start : u_v_start].reshape((Y_HEIGHT, Y_WIDTH))
U_V = yuv_data[u_v_start : u_v_end].reshape((U_V_SIZE, 2))
V[frame_idx, :, :] = U_V[:, 0].reshape((U_V_HEIGHT, U_V_WIDTH))
U[frame_idx, :, :] = U_V[:, 1].reshape((U_V_HEIGHT, U_V_WIDTH))
return Y, U, V
def np_yuv2rgb(Y,U,V):
bgr_data = np.zeros((IMG_HEIGHT, IMG_WIDTH, 3), dtype=np.uint8)
V = np.repeat(V, 2, 0)
V = np.repeat(V, 2, 1)
U = np.repeat(U, 2, 0)
U = np.repeat(U, 2, 1)
c = (Y-np.array([16])) * 298
d = U - np.array([128])
e = V - np.array([128])
r = (c + 409 * e + 128) // 256
g = (c - 100 * d - 208 * e + 128) // 256
b = (c + 516 * d + 128) // 256
r = np.where(r < 0, 0, r)
r = np.where(r > 255,255,r)
g = np.where(g < 0, 0, g)
g = np.where(g > 255,255,g)
b = np.where(b < 0, 0, b)
b = np.where(b > 255,255,b)
bgr_data[:, :, 2] = r
bgr_data[:, :, 1] = g
bgr_data[:, :, 0] = b
return bgr_data
def yuv2rgb(Y, U, V):
bgr_data = np.zeros((IMG_HEIGHT, IMG_WIDTH, 3), dtype=np.uint8)
for h_idx in range(Y_HEIGHT):
for w_idx in range(Y_WIDTH):
y = Y[h_idx, w_idx]
u = U[int(h_idx // 2), int(w_idx // 2)]
v = V[int(h_idx // 2), int(w_idx // 2)]
c = (y - 16) * 298
d = u - 128
e = v - 128
r = (c + 409 * e + 128) // 256
g = (c - 100 * d - 208 * e + 128) // 256
b = (c + 516 * d + 128) // 256
bgr_data[h_idx, w_idx, 2] = 0 if r < 0 else (255 if r > 255 else r)
bgr_data[h_idx, w_idx, 1] = 0 if g < 0 else (255 if g > 255 else g)
bgr_data[h_idx, w_idx, 0] = 0 if b < 0 else (255 if b > 255 else b)
return bgr_data
if __name__ == '__main__':
import time
yuv = "request/YUV/2021-05-06/test.yuv"
frames = int(os.path.getsize(yuv) / IMG_SIZE)
with open(yuv, "rb") as yuv_f:
time1 = time.time()
yuv_bytes = yuv_f.read()
yuv_data = np.frombuffer(yuv_bytes, np.uint8)
# Y, U, V = from_I420(yuv_data, frames)
# Y, U, V = from_YV12(yuv_data, frames)
# Y, U, V = from_NV12(yuv_data, frames)
Y, U, V = from_NV21(yuv_data, frames)
rgb_data = np.zeros((IMG_HEIGHT, IMG_WIDTH, 3), dtype=np.uint8)
for frame_idx in range(frames):
# bgr_data = yuv2rgb(Y[frame_idx, :, :], U[frame_idx, :, :], V[frame_idx, :, :]) # for
bgr_data = np_yuv2rgb(Y[frame_idx, :, :], U[frame_idx, :, :], V[frame_idx, :, :]) # numpy
time2 = time.time()
print(time2 - time1)
if bgr_data is not None:
cv2.imwrite("frame_{}.jpg".format(frame_idx), bgr_data)
frame_idx +=1
补充 LUT
1D LUT只能控制gamma值、RGB平衡(灰阶)和白场(white point),而3D LUT能以全立体色彩空间的控制方式影响色相、饱和度、亮度等。简单描述来说3D LUT可以影响到颜色,而1D LUT只能影响亮度值。3D LUT变动某个颜色值,都会对三个颜色值造成影响,即任何一个颜色的改变都会使其他颜色做出相应的改变。
推荐阅读
2 附录
2.1 图像矩
矩是统计学的一个概念(pencv中的图像矩(空间矩,中心矩,归一化中心矩,Hu矩))
图像矩(Image moments)是指图像的某些特定像素灰度的加权平均值(矩),或者是图像具有类似功能或意义的属性。
图像矩通常用来描述分割后的图像对象。可以通过图像的矩来获得图像的部分性质,包括面积(或总体亮度),以及有关几何中心和方向的信息 。
例如工业缺陷检测中(实操教程|使用计算机视觉算法检测钢板中的焊接缺陷),使用图像矩测量缺陷严重性
什么叫图像的矩,在数字图像处理中有什么作用? - 谢博琛的回答 - 知乎
https://www.zhihu.com/question/26803016/answer/888699124
0 阶矩
m
00
m_{00}
m00:目标区域的面积(Area)
1 阶矩
m
01
,
m
10
m_{01}, m_{10}
m01,m10:目标区域的质心(Centroid)
2 阶矩
m
02
,
m
20
,
m
11
m_{02}, m_{20}, m_{11}
m02,m20,m11:即惯性矩,可计算目标图像的方向
3 阶矩
m
03
,
m
30
,
m
12
,
m
21
m_{03}, m_{30}, m_{12}, m_{21}
m03,m30,m12,m21:目标区域的方位和斜度,反应目标的扭曲
Hu 矩:目标区域往往伴随着空间变换(平移,尺度,旋转),所以需要在普通矩的基础上构造出具备不变性的矩组
中心矩:构造平移不变性
2.2 马氏距离
马氏距离(Mahalanobis Distance)是度量学习中一种常用的距离指标,同欧氏距离、曼哈顿距离、汉明距离等一样被用作评定数据之间的相似度指标。但却可以应对高维线性分布的数据中各维度间非独立同分布的问题。
2.3 随机生成图片
import cv2
import numpy as np
import os
randomByteArray = bytearray(os.urandom(120000)) # 0~256
print(type(randomByteArray))
flatArray = np.array(randomByteArray)
grayImage = flatArray.reshape(300, 400)
cv2.imwrite("RandomGray.jpg", grayImage)
bgrImage = flatArray.reshape(100,400,3)
cv2.imwrite("RandomColor.jpg",bgrImage)
生成各种纯色的背景
Array = np.array(bytearray(os.urandom(640*640*3))).reshape(640, 640, 3)
for i in range(0,256, 3):
for j in range(0, 256, 3):
for k in range(0,256, 3):
Array[..., 0] = i
Array[..., 1] = j
Array[..., 2] = k
cv2.imwrite("xxx.jpg", Array)
2.4 jpg
JPEG之所以成为有损压缩技术,是因为使用了量化
3 OpenCV 的知识体系
来自 难以置信!一篇文章就梳理清楚了 Python OpenCV 的知识体系
一、OpenCV 图像读取,显示,保存
涉及需要学习的函数有 cv2.imread()、cv2.namedWindow()、cv2.imshow()、cv2.imwrite()、cv2.destroyWindow()、cv2.destroyAllWindows()、 cv2.imshow()、cv2.cvtColor()、cv2.imwrite()、cv2.waitKey()。
二、摄像头和视频读取,保存
第一个要重点学习 VideoCapture 类,该类常用的方法有:
open() 函数;
isOpened() 函数;
release() 函数;
grab() 函数;
retrieve() 函数;
get() 函数;
set() 函数;
三、OpenCV 常用数据结构和颜色空间
Point 类、Rect 类、Size 类、Scalar 类
BGR 颜色空间、HSV/HLS 颜色空间、Lab 颜色空间
四、OpenCV 常用绘图函数
cv2.line();
cv2.circle();
cv2.rectangle();
cv2.ellipse();
cv2.fillPoly();
cv2.polylines();
cv2.putText()。
五、OpenCV 界面事件操作之鼠标与滑动条
cv2.setMouseCallback()
滑动条涉及两个函数,分别是:cv2.createTrackbar() 和 cv2.getTrackbarPos()。
六、图像像素、通道分离与合并
cv2.split(),
cv2.merge()。
七、图像逻辑运算
cv2.add();
cv2.addWeighted();
cv2.subtract();
cv2.absdiff();
cv2.bitwise_and();
cv2.bitwise_not();
cv2.bitwise_xor()。
八、图像几何变换
图像缩放 cv2.resize();
图像平移 cv2.warpAffine();
图像旋转 cv2.getRotationMatrix2D();
图像转置 cv2.transpose();
图像镜像 cv2.flip();
图像重映射 cv2.remap()。
九、图像滤波
线性滤波:方框滤波、均值滤波、高斯滤波,
非线性滤波:中值滤波、双边滤波,
方框滤波 cv2.boxFilter();
均值滤波 cv2.blur();
高斯滤波 cv2.GaussianBlur();
中值滤波 cv2.medianBlur();
双边滤波 cv2.bilateralFilter()。
十、图像固定阈值与自适应阈值
固定阈值:cv2.threshold();
自适应阈值:cv2.adaptiveThreshold()。
十一、图像膨胀腐蚀
膨胀 cv2.dilate();
腐蚀 cv2.erode()。
cv2.morphologyEx()
十二、边缘检测
Canny 算子,Canny 边缘检测函数 cv2.Canny();
Sobel 算子,Sobel 边缘检测函数 cv2.Sobel();
Scharr 算子,Scharr 边缘检测函数 cv2.Scahrr() ;
Laplacian 算子,Laplacian 边缘检测函数 cv2.Laplacian()。
十三、霍夫变换
标准霍夫变换、多尺度霍夫变换 cv2.HoughLines() ;
累计概率霍夫变换 cv2.HoughLinesP() ;
霍夫圆变换 cv2.HoughCricles() 。
十四、图像直方图计算及绘制
直方图均衡化 cv2.equalizeHist();
直方图对比 cv2.compareHist();
反向投影 cv2.calcBackProject()。
十五、模板匹配
模板匹配 cv2.matchTemplate();
矩阵归一化 cv2.normalize();
寻找最值 cv2.minMaxLoc()。
十六、轮廓查找与绘制
查找轮廓 cv2.findContours();
绘制轮廓 cv2.drawContours() 。
十七、 轮廓特征属性及应用
寻找凸包 cv2.convexHull() 与 凸性检测 cv2.isContourConvex();
轮廓外接矩形 cv2.boundingRect();
轮廓最小外接矩形 cv2.minAreaRect();
轮廓最小外接圆 cv2.minEnclosingCircle();
轮廓椭圆拟合 cv2.fitEllipse();
逼近多边形曲线 cv2.approxPolyDP();
计算轮廓面积 cv2.contourArea();
计算轮廓长度 cv2.arcLength();
计算点与轮廓的距离及位置关系 cv2.pointPolygonTest();
形状匹配 cv2.matchShapes()。
十八、高级部分-分水岭算法及图像修补
cv2.watershed()
cv2.inpaint()
十九、GrabCut & FloodFill 图像分割、角点检测
GrabCut 算法 cv2.grabCut();
漫水填充算法 cv2.floodFill();
Harris 角点检测 cv2.cornerHarris();
Shi-Tomasi 角点检测 cv2.goodFeaturesToTrack();
亚像素角点检测 cv2.cornerSubPix()。
二十、特征检测与匹配
“FAST” FastFeatureDetector;
“STAR” StarFeatureDetector;
“SIFT” SIFT(nonfree module) Opencv3 移除,需调用 xfeature2d 库;
“SURF” SURF(nonfree module) Opencv3 移除,需调用 xfeature2d 库;
“ORB” ORB Opencv3 移除,需调用 xfeature2d 库;
“MSER” MSER;
“GFTT” GoodFeaturesToTrackDetector;
“HARRIS” (配合 Harris detector);
“Dense” DenseFeatureDetector;
“SimpleBlob” SimpleBlobDetector。
二十一、 OpenCV 应用部分之运动物体跟踪与人脸识别
meanShift 跟踪算法 cv2.meanShift();
CamShift 跟踪算法 cv2.CamShift()。