OpenCV-Python
基本操作
创建图像
新建
- 灰度图等价于numpy的二维矩阵
注意
dtype应为np.unit8img = np.zeros((720,1280), dtype=np.uint8) - 创建和原图一样大小的遮罩
img = np.zeros(img.shape[:2], dtype=np.uint8) - 彩色图像默认通道顺序为BGR,numpy三维矩阵,黑色背景
img = np.zeros((720,1280,3), dtype=np.uint8) - 白色背景
img = np.full((720,1280,3), 255, dtype=np.uint8) - 给定一个单色图像,生成一个相同尺寸的BGR图像用于调试(mask前的
*为解包符号)
img_debug = np.full((*mask.shape[:2], 3), 255, dtype=np.uint8)读取
读取图像
- 参数
cv2.IMREAD_GRAYSCALE读取为灰度,不带参数默认读取为BGR图像img = cv2.imread("./mask.png", cv2.IMREAD_GRAYSCALE) - 获取彩色图像的长宽:
h, w = img.shape[:2]
读取视频并逐帧访问
- 读入视频
vidcap = cv2.VideoCapture('source.mp4') - 逐帧访问
success,image = vidcap.read() - 参考
import cv2 vidcap = cv2.VideoCapture('source.mp4') success,image = vidcap.read() count = 0 while success: cv2.imwrite("frame%d.png" % count, image) # save frame as JPEG file success,image = vidcap.read() print('Read a new frame: ', success) count += 1
写入
写入图片
cv2.imwrite(filename, img)颜色空间/数据格式
图像尺寸
- 通过img.shape获取numpy矩阵尺寸,为(高,宽,通道数)
默认数据类型
-
OpenCV默认的彩色图像三个通道依次为B、G、R
-
灰度转RGB 转换时将会直接将一个通道的数值复制三份
img2 = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)- 转HSV色彩空间
从BGR色彩空间转换为HSV,并取出某个特定通道。H:hue色调,S:saturation饱和度,V:value明度
hsv = cv2.cvtColor(self.img, cv2.COLOR_BGR2HSV) h = hsv[:,:,0] s = hsv[:,:,1] v = hsv[:,:,2]
常用BGR颜色
- 青色:(255, 255, 0)
- 黄色:(0, 255, 255)
- 紫色:(255, 0, 255)
- 白色:(255, 255, 255)
- 黑色:(0, 0, 0)
与其它包所使用的数据类型相互转换
skimage与OpenCV image的互相转换
- skimage转OpenCV image分为两个步骤:
- skimage的mask可能是
numpy.bool_类型的,先将其转换为数值,转换方式为同理处理mask时可以将其它值替换为0/1mask = np.where(sk_imge==True, 255, 0) np.where转换后数据类型为numpy.int32,使用img.astype(np.uint8)转换为OpenCV的图像数据类型
- skimage的mask可能是
与Matplotlib或者标准RGB格式的之间的转换
- 色彩空间的转换:
cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
显示图像
-
显示图像可以用
imshow('title', img) -
使用Matplotlib
- 需将BGR格式转为RGB格式,参考
- 灰度图像以灰色而不是伪彩色显示
plt.imshow(img, cmap='Greys_r', interpolation='nearest') -
鼠标悬浮在像素上时显示对应点的值(使用Matplotlib):
import matplotlib.pyplot as plt
plt.imshow(masked_image)
plt.show()- 显示一张图片并等待ESC键退出
def display_img(img):
cv2.imshow('image', img)
show_window = True
while show_window:
k = cv2.waitKey(0) & 0xFF
if k == 27:#ESC
cv2.destroyAllWindows()
show_window = False- 持续显示(视频)
while True:
## get image here
cv2.imshow('image', img)
if cv2.waitKey(25) & 0xFF == 27:
break选择ROI
- 使用
selectROI(img)函数,返回为(x, y, dx, dy)
import cv2
import numpy as np
if __name__ == '__main__':
im = cv2.imread("image.png")
r = cv2.selectROI(im)
print(r)
roi_rect = [[int(r[0]), int(r[1])], [int(r[0]+r[2]),int(r[1]+r[3])]] # [[x1,y1],[x2,y2]]
print(roi_rect)
imCrop = im[roi_rect[0][1]:roi_rect[1][1],roi_rect[0][0]:roi_rect[1][0],:] # [[y1,y2],[x1,x2]]
cv2.imshow("Imgage", imCrop)
cv2.waitKey(0)缩放图像
- 使用
cv2.resize()
cropped_img = cv2.resize(img[y1:y2,x1:x2], (640,480))裁剪图像、将处理后的ROI放回原图像
- 直接通过numpy矩阵进行操作
img_range = [[x1,y1],[x2,y2]]
roi = img[img_range[0][1]:img_range[1][1], img_range[0][0]:img_range[1][0], :]
roi_updated = YOUR_CV_FUNC(roi)
img_update = copy.deepcopy(img)
img_update[img_range[0][1]:img_range[1][1], img_range[0][0]:img_range[1][0], :] = roi_updated拼接图像
- 直接通过numpy矩阵进行操作
- 水平方向拼接
hori = np.concatenate((img1, img2), axis=1)- 竖直方向拼接
verti = np.concatenate((img1, img2), axis=0)直方图
- 注意对一个numpy矩阵求最值时返回的类型将为
<class 'numpy.uint16'>或者是numpy的浮点数类型,需进行类型转换后再作为calcHist的参数
x_max = int(img.max())
hist = cv2.calcHist([img],[0],None,[x_max],[0,x_max])
y_max = int(hist[1:,].max())
plt.plot(hist[1:,])
plt.xlim([0,x_max])
plt.ylim([0,y_max])
plt.show()用矩阵进行图像的旋转拉伸位移与点的变换
- 得到一个旋转角度(单位为度)以及旋转的中心-> 得到变换矩阵 -> 调用
warpAffine进行旋转
rotate_matrix = cv2.getRotationMatrix2D(center=center, angle=angle, scale=1)
rotated_image = cv2.warpAffine(src=img, M=rotate_matrix, dsize=(width, height))- 找到点在新的空间中的投射
np.dot(a,b)和a.dot(b)的结果是一样的
r_mat = cv2.getRotationMatrix2D((100/2, 300/2),-30,1)
# points
points = np.array([[35., 0.],
[175., 0.],
[105., 200.],
[105., 215.],
])
# add ones
ones = np.ones(shape=(len(points), 1))
points_ones = np.hstack([points, ones])
# transform points
transformed_points = np.dot(r_mat, points_ones.T).T效率问题
使用Numpy操作图像的效率问题
- Numpy操作矩阵的效率偏低,如果使用Numpy的索引(index)遍历修改整个图像,可能需要一秒(如图像大小为720x1280x3)