文章目录
- 安装
- 0 基础操作
- 获取像素值,修改像素值
- 查看图像属性
- 1 RGB / HSV / HSL / YUV
- 1.1 RGB
- 1.2 HSV
- 1.3 HSL
- 1.4 YUV
- 补充 LUT
- 2 画框画圈
- 2.1 画矩形框
- 2.2 画圆 / 点
- 2.3 椭圆
- 3 resize
- 4 Selective Search
- 5 附录
- 5.1 图像矩
- 5.2 马氏距离
- 5.3 随机生成图片
- 5.4 jpg
- 5.5 显示两张图片
最近一次修改时间为:2020-09-09
辅助手册 OpenCV 帮助文档
安装
OpenCV
是 Open Source Computer Vision
的缩写,由英特尔公司于1999年推出。它最初是用 C/ C++
编写的,所以你可能会看到更多用 C 语言而不是 Python 编写的教程。但现在它在 Python 中也被广泛用于计算机视觉。
在安装 anaconda 的情况下,conda install opencv
即可!
安装指定版本提示找不到的话,可以试试先 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%(白)。
# Transform the image into HSV
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Plot the converted images
plt.axis("off")
plt.imshow(img_hsv)
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
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 画矩形框
# Copy the image
img_rgb_copy = img_rgb.copy()
# Draw a rectangle
cv2.rectangle(img_rgb_copy, pt1 = (405, 90), pt2 =(740, 510),
color = (255, 0, 0), thickness = 5)
plt.axis("off")
plt.imshow(img_rgb_copy)
pt1 是左上角坐标,pt2 是右下角坐标,坐标可以用电脑自带的画图工具获取,直接 copy 过来就行!
补充1:如果要画成 mask 块,也即全填充的形式,则把 thickness
参数设置为 -1 即可
补充2:如果要把区域裁剪出来,可以借助 numpy 的切片操作,例如
import cv2
img_pth = "/file2/yanmeng/human/ocean3_115/images/ocean_17_55034.jpg"
img = cv2.imread(img_pth)
box = (606, 182, 682, 360) # (x0, y0, x1, y1)
crop_img = img[box[1]:box[3], box[0]:box[2]] # [y0:y1, x0:x1]
cv2.imshow("1", img)
cv2.imshow("2", crop_img)
cv2.waitKey(10000)
cv2.destroyAllWindows()
复制 RoI 区域
import cv2
img = cv2.imread("1.jpg")
bag = img[840:1004, 830:1032]
img[840:1004, 830+200:1032+200] = bag
cv2.imwrite("2.jpg", img)
快乐加倍!!!
2.2 画圆 / 点
void cvCircle( CvArr* img, CvPoint center, int radius, CvScalar color,
int thickness=1, int line_type=8, int shift=0 );
- img:图像。
- center:圆心坐标。
- radius:圆形的半径。
- color:线条的颜色。
t- hickness:如果是正数,表示组成圆的线条的粗细程度。否则,表示圆是否被填充。 - line_type:线条的类型。见 cvLine 的描述
- shift:圆心坐标点和半径值的小数点位数。
img_rgb_copy = img_rgb.copy()
# Draw a circle
cv2.circle(img_rgb_copy, center = (200, 280), radius =80,
color = (0,0,255), thickness = 5)
plt.axis("off")
plt.imshow(img_rgb_copy)
一起画,哈哈哈,我也是周伯通了,左手画圆,右手画框!
2.3 椭圆
绘制椭圆
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = np.zeros((512,512,3),np.uint8)#生成一个空彩色图像
cv2.ellipse(img=img,
center=(256,256),
axes=(150,100),
angle=30,
startAngle=45,
endAngle=180,
color=(0,0,255),
thickness=-1)
#注意最后一个参数-1,表示对图像进行填充,默认是不填充的,如果去掉,只有椭圆轮廓了
cv2.imshow("1", img)
cv2.waitKey(0)
3 resize
cv2.resize(InputArray src, OutputArray dst, Size, fx, fy, interpolation)
- InputArray src:输入图片
- OutputArray dst :输出图片
- Size:输出图片尺寸——(w,h)
- fx, fy:沿 x 轴,y 轴的缩放系数
- interpolation:插入方式
4 Selective Search
RCNN 和 Fast RCNN 都用的是 SS,目前被主流的 Faster RCNN 的 Region Proposal Network 替代!我们重温下经典!
我用的 jupyter notebook,所以要克服下 opencv 的 cv2.imshow()
问题
import cv2
import matplotlib.pyplot as plt
if __name__ == '__main__':
cv2.setUseOptimized(True);
cv2.setNumThreads(4);
# read image
im = cv2.imread('/root/userfolder/Experiment/1.png')
# resize image
newHeight = 200
newWidth = int(im.shape[1] * 200 / im.shape[0])
im = cv2.resize(im, (newWidth, newHeight))
#cv2.imshow("input", im)
# jupyter notebook
#img = im[:,:,::-1] # 必须为 ::-1
#plt.imshow(im)
# 创建算法+设置输入图像
ss = cv2.ximgproc.segmentation.createSelectiveSearchSegmentation()
ss.setBaseImage(im)
# 使用SS快速版本
ss.switchToSelectiveSearchFast()
# 执行SS
rects = ss.process()
print('Total Number of Region Proposals: {}'.format(len(rects)))
# 推荐100个ROI
numShowRects = 100
imOut = im.copy()
# 显示前100个区域外接矩形框
for i, rect in enumerate(rects):
if i < numShowRects:
x, y, w, h = rect
cv2.rectangle(imOut, (x, y), (x + w, y + h), (0, 255, 0), 1, cv2.LINE_AA)
else:
break
# show output
"""
cv2.imshow("SS-Demo", imOut)
cv2.waitKey(0)
cv2.destroyAllWindows()
"""
# jupyter notebook
img = imOut[:,:,::-1] # 必须为 ::-1
plt.xticks(())
plt.yticks(())
plt.imshow(img)
处理前
处理后
5 附录
5.1 图像矩
矩是统计学的一个概念(pencv中的图像矩(空间矩,中心矩,归一化中心矩,Hu矩))
图像矩(Image moments)是指图像的某些特定像素灰度的加权平均值(矩),或者是图像具有类似功能或意义的属性。
图像矩通常用来描述分割后的图像对象。可以通过图像的矩来获得图像的部分性质,包括面积(或总体亮度),以及有关几何中心和方向的信息 。
例如工业缺陷检测中(实操教程|使用计算机视觉算法检测钢板中的焊接缺陷),使用图像矩测量缺陷严重性
什么叫图像的矩,在数字图像处理中有什么作用?
0 阶矩 :目标区域的面积(Area)
1 阶矩 :目标区域的质心(Centroid)
2 阶矩 :即惯性矩,可计算目标图像的方向
3 阶矩 :目标区域的方位和斜度,反应目标的扭曲
Hu 矩:目标区域往往伴随着空间变换(平移,尺度,旋转),所以需要在普通矩的基础上构造出具备不变性的矩组
中心矩:构造平移不变性
5.2 马氏距离
马氏距离(Mahalanobis Distance)是度量学习中一种常用的距离指标,同欧氏距离、曼哈顿距离、汉明距离等一样被用作评定数据之间的相似度指标。但却可以应对高维线性分布的数据中各维度间非独立同分布的问题。
5.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)
5.4 jpg
JPEG之所以成为有损压缩技术,是因为使用了量化
5.5 显示两张图片
opencv同时显示两张图片
采用 hstack 堆叠在一起
import cv2
import numpy as np
def img_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
img1=cv2.imread(path1)
img2=cv2.imread(path2)
img1 = cv2.resize(img1, (640, 640))
img2 = cv2.resize(img2, (640, 640))
new_img = np.hstack([img1, img2])
img_show('new_img', new_img)