引入
import cv2 image = cv2.imread("timg.jpg") #第二个参数可以选择色彩,例如灰色:cv2.IMREAD_GRAYSCALE # 显示图片 cv2.imshow("timg1",image) # 等待键盘输入,否则一闪而过 cv2.waitKey() # cv2.imwrite("名字",image) cv2.destroyAllWindows()# 关闭所有窗口
使用opencv进行图片svd压缩:
import cv2 image = cv2.imread("timg.jpg") # 降低图片尺寸 image = cv2.resize(image,(264,264)) image = image[:,:,[2,1,0]] # 使用SVD分解 image_gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) import numpy as np k = int(image_gray.shape[1]/4) u1,s1,v1 = np.linalg.svd(image_gray) image1 = image_gray.dot(v1[:k,:].T) import matplotlib.pyplot as plt fig,sns = plt.subplots(3,2) plt.gray() sns[0,0].imshow(image) sns[0,1].imshow(image_gray) ss = np.diag(s1) image2 = u1[:,:k].dot(ss[:k,:k].dot(v1[:k,:])) sns[1,0].imshow(image2) sns[1,1].imshow(image_gray-image2) sns[2,0].imshow(image1) sns[2,1].imshow(image1) plt.show()
视频的录制和播放
import numpy as np import cv2 # 类似于打开摄像头 cap = cv2.VideoCapture(0) # 生成一个视频写入对象 fourcc = cv2.VideoWriter_fourcc(*'XVID') out = cv2.VideoWriter('output.avi',fourcc, 20.0, (640,480)) while True: ret, frame = cap.read() # 展示在桌面 cv2.imshow('frame', frame) # 写入文件当中 out.write(frame) # 当按键按下q的时候停止 if cv2.waitKey(1) & 0xFF == ord('q'): break # 释放摄像头 cap.release() # 释放写入器 out.release() # 关闭显示器 cv2.destroyAllWindows()
与从摄像头中捕获一样,你只需要把设备索引号改成视频文件的名字。在播放每一帧时,使用 cv2.waiKey() 设置适当的持续时间。如果设置的太低视频就会播放的非常快,如果设置的太高就会播放的很慢(你可以使用这种方法控制视频的播放速度)。通常情况下 设置为25 毫秒。
-
cv2.CAP_PROP_POS_MSEC: 视频文件的当前位置(ms)
-
cv2.CAP_PROP_POS_FRAMES: 从0开始索引帧,帧位置。
-
cv2.CAP_PROP_POS_AVI_RATIO:视频文件的相对位置(0表示开始,1表示结束)
-
cv2.CAP_PROP_FRAME_WIDTH: 视频流的帧宽度。
-
cv2.CAP_PROP_FRAME_HEIGHT: 视频流的帧高度。
-
cv2.CAP_PROP_FPS: 帧率
-
cv2.CAP_PROP_FOURCC: 编解码器四字符代码
-
cv2.CAP_PROP_FRAME_COUNT: 视频文件的帧数
-
cv2.CAP_PROP_FORMAT: retrieve()返回的Mat对象的格式。
-
cv2.CAP_PROP_MODE: 后端专用的值,指示当前捕获模式
-
cv2.CAP_PROP_BRIGHTNESS:图像的亮度,仅适用于支持的相机
-
cv2.CAP_PROP_CONTRAST: 图像对比度,仅适用于相机
-
cv2.CAP_PROP_SATURATION:图像饱和度,仅适用于相机
-
cv2.CAP_PROP_HUE: 图像色调,仅适用于相机
-
cv2.CAP_PROP_GAIN: 图像增益,仅适用于支持的相机
-
cv2.CAP_PROP_EXPOSURE: 曝光,仅适用于支持的相机
-
cv2.CAP_PROP_CONVERT_RGB:布尔标志,指示是否应将图像转换为RGB
import cv2 cap = cv2.VideoCapture('output.avi') while cap.isOpened(): ret,frame = cap.read() cv2.imshow("frame",frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()
绘图函数
import numpy as np import cv2 # 注意这里必须是uint8,否则报错 img=np.zeros((512,512,3), np.uint8) # 画一条直线 起点 终点 颜色 宽度 cv2.line(img, (0, 0), (511, 511), (255, 0, 0), 5) # 画一个矩形 左上 右下 颜色 宽度 cv2.rectangle(img,(384,0),(510,128),(0,255,0),3) # 画一个圆形 中心 半径 颜色 是否填充 cv2.circle(img,(447,63), 63, (0,0,255), -1) # 画一个椭圆 中心 (长轴,短轴) # 整个图形沿逆时针反向旋转角度 # 顺时针从起始点到中止点画图, 颜色 是否填充 cv2.ellipse(img,(256,256),(100,50),0,0,180,255,-1) pts=np.array([[10,5],[20,30],[70,20],[50,10]], np.int32) pts=pts.reshape((-1,1,2)) #可以被用来画很多条线。只需要把想要画的线放在一个列表中 # 画板 列表 是否闭合 颜色 cv2.polylines(img,[pts],True,(0,255,255)) # 添加文字 font=cv2.FONT_HERSHEY_SIMPLEX cv2.putText(img,'OpenCV',(10,500), font, 4,(255,255,255),2) # 这里 reshape 的第一个参数为-1, 表明这一维的长度是根据后面的维度的计算出来的。 cv2.imshow("img", img) cv2.waitKey(0) cv2.destroyAllWindows()
• img:你想要绘制图形的那幅图像。 • color:形状的颜色。以 RGB 为例,需要传入一个元组,例如:(255,0,0) 代表蓝色。对于灰度图只需要传入灰度值。 • thickness:线条的粗细。如果给一个闭合图形设置为 -1,那么这个图形 就会被填充。默认值是 1. • linetype:线条的类型,8 连接,抗锯齿等。默认情况是 8 连接。cv2.LINE_AA 为抗锯齿,这样看起来会非常平滑。
鼠标当画笔
import cv2 # 查看所有的鼠标事件 events=[i for i in dir(cv2) if 'EVENT'in i] print(events) ['EVENT_FLAG_ALTKEY', 'EVENT_FLAG_CTRLKEY', 'EVENT_FLAG_LBUTTON', 'EVENT_FLAG_MBUTTON', 'EVENT_FLAG_RBUTTON', 'EVENT_FLAG_SHIFTKEY', 'EVENT_LBUTTONDBLCLK', 'EVENT_LBUTTONDOWN', 'EVENT_LBUTTONUP', 'EVENT_MBUTTONDBLCLK', 'EVENT_MBUTTONDOWN', 'EVENT_MBUTTONUP', 'EVENT_MOUSEHWHEEL', 'EVENT_MOUSEMOVE', 'EVENT_MOUSEWHEEL', 'EVENT_RBUTTONDBLCLK', 'EVENT_RBUTTONDOWN', 'EVENT_RBUTTONUP'] Process finished with exit code 0
下面使用双击函数实现如下程序:
import cv2 import numpy as np def draw_circle(event, x, y, flags, param): # 当使用者双击屏幕的时候生成一个空心圆形 if event == cv2.EVENT_LBUTTONDBLCLK: # 生成rgb的值 r = np.random.randint(0,255) g = np.random.randint(0,255) b = np.random.randint(0,255) # 指定特定的位置进行画图 cv2.circle(img, (x, y), 100, (r, g, b), 0) # 创建图像与窗口并将窗口与回调函数绑定 img = np.zeros((512, 512, 3), np.uint8) cv2.namedWindow('image') # 传递一个回调函数 cv2.setMouseCallback('image', draw_circle) while (1): cv2.imshow('image', img) if cv2.waitKey(20) & 0xFF == 27: break cv2.destroyAllWindows()
下个程序要完成的任务是根据我们选择的模式在拖动鼠标时绘制矩形或者是圆圈(就像画图程序中一样)。所以我们的回调函数包含两部分,一部分画矩形,一部分画圆圈。这是一个典型的例子他可以帮助我们更好理解与构建人机交互式程序,比如物体跟踪,图像分割等。
import cv2 import numpy as np # 当鼠标按下时变为 True drawing = False ix, iy = -1, -1 # 创建回调函数 def draw_circle(event, x, y, flags, param): global ix, iy, drawing, mode # 当按下左键是返回起始位置坐标 if event == cv2.EVENT_LBUTTONDOWN: drawing = True ix, iy = x, y # 当鼠标左键按下并移动是绘制图形。event 可以查看移动,flag 查看是否按下 elif event == cv2.EVENT_MOUSEMOVE and flags == cv2.EVENT_FLAG_LBUTTON: if drawing == True: cv2.circle(img, (x, y), 3, (0, 0, 255), -1) # 当鼠标松开停止绘画。 elif event == cv2.EVENT_LBUTTONUP: drawing == False img = np.zeros((512, 512, 3), np.uint8) cv2.namedWindow('image') cv2.setMouseCallback('image', draw_circle) while (1): cv2.imshow('image', img) k = cv2.waitKey(1) & 0xFF if k == 27: break
cv2.rectangle(img, (ix, iy), (x, y), (0, 255, 0), -1)
完整的画板程序,点住鼠标左键划线,点住鼠标右键画方块:
import cv2 import numpy as np drawing = False ix, iy = -1, -1 def nothing(x): pass def draw_circle(event,x,y,flags,param): r = cv2.getTrackbarPos('R','image') g = cv2.getTrackbarPos('G','image') b = cv2.getTrackbarPos('B','image') color = (b,g,r) s = cv2.getTrackbarPos('eraser','image') thin = cv2.getTrackbarPos('thin','image') if s== 1: color = (255,255,255) global ix,iy,drawing if event == cv2.EVENT_LBUTTONDOWN: drawing = True ix,iy = x,y elif event == cv2.EVENT_MOUSEMOVE and flags == cv2.EVENT_FLAG_LBUTTON: if drawing == True: cv2.circle(img,(x,y),thin,color,-1) elif event == cv2.EVENT_LBUTTONUP: drawing == False elif event == cv2.EVENT_MOUSEMOVE and flags == cv2.EVENT_FLAG_RBUTTON: if drawing == True: cv2.rectangle(img, (ix, iy), (x, y), color, -1) img = np.zeros((512,512,3), np.uint8) img[:] = 255 cv2.namedWindow('image') cv2.createTrackbar('R','image',0,255,nothing) cv2.createTrackbar('G','image',0,255,nothing) cv2.createTrackbar('B','image',0,255,nothing) cv2.createTrackbar('eraser','image',0,1,nothing) cv2.createTrackbar('thin','image',1,50,nothing) cv2.setMouseCallback('image', draw_circle) while(1): cv2.imshow('image',img) k = cv2.waitKey(1) & 0xFF if k == 27: break
图像的基本操作
获取像素值并修改 • 获取图像的属性(信息)
import cv2 import numpy as np img = cv2.imread("timg.jpg") # Numpy 是经过优化了的进行快速矩阵运算的软件包。所以我们不推荐 # 逐个获取像素值并修改,这样会很慢,能有矩阵运算就不要用循环。 #更改某一个像素 print(img[100,100]) img[100,100] = [255,255,255] print(img[100,100]) # 显示形状 行,列,通道 print(img.shape) # 像素个数 print(img.size) # 数据类型 print(img.dtype)
• 图像的 ROI()
import cv2 import numpy as np img = cv2.imread("timg.jpg") # cv2.imshow("img",img) ball=img[270:290,330:380] img[273:293,250:300]=ball cv2.imshow("ball",img) cv2.waitKey() cv2.destroyAllWindows()
图片比较诡异,自己运行
• 图像通道的拆分及合并
# cv2.split() 是一个比较耗时的操作。只有真正需要时才用它,能用Numpy 索引就尽量用。 b,g,r=cv2.split(img) img=cv2.merge((b,g,r))
•为图像扩边
import cv2 import numpy as np from matplotlib import pyplot as plt import matplotlib as mpl mpl.rcParams["font.sans-serif"] = ["SimHei"] BLUE=[255,0,0] img1=cv2.imread('opencv_logo.jpg') # 重复最后一个元素例如: aaaaaa|abcdefgh|hhhhhhh replicate = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REPLICATE) # 边界元素的镜像。比如: fedcba|abcdefgh|hgfedcb reflect = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT) # 跟上面一样,但稍作改动。例如: gfedcb|abcdefgh|gfedcba reflect101 = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT_101) # 上面的一部分映射到下面 wrap = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_WRAP) # 加上轮廓 constant= cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_CONSTANT,value=BLUE) plt.subplot(231),plt.imshow(img1,'gray'),plt.title('原图') 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()
图像上的算术运算
图像的加法
OpenCV 中的加法与 Numpy 的加法是有所不同的。OpenCV 的加法 是一种饱和操作,而 Numpy 的加法是一种模操作。
import cv2 import numpy as np x = np.uint8([250]) y = np.uint8([10]) print(cv2.add(x,y)) # [[255]]
图像的混合
这其实也是加法,但是不同的是两幅图像的权重不同,这就会给人一种混 合或者透明的感觉。图像混合的计算公式如下:
import cv2 img1=cv2.imread('hz.jpg') img2=cv2.imread('opencv_logo.jpg') dst=cv2.addWeighted(img1,0.7,img2,0.3,0) import matplotlib.pyplot as plt img1 = img1[:,:,[2,1,0]] plt.subplot(221),plt.imshow(img1,'gray') plt.subplot(222),plt.imshow(img2,'gray') plt.subplot(223),plt.imshow(dst,'gray') dst=cv2.addWeighted(img1,0.7,img2,0.3,0) plt.subplot(224),plt.imshow(dst,'gray') plt.show()
按位运算:
这里包括的按位操作有:AND(与),OR(或),NOT(非),XOR (异或)等。当我们提取图像的一部分,选择非矩形 ROI 时这些操作会很有用。下面的例子就是教给我们如何改变一幅图的特定区域。我想把 OpenCV 的标志放到另一幅图像上。如果我使用加法,颜色会改变,如果使用混合,会得到透明效果,但是我不想要透明。如果他是矩形我可以象上一章那样使用 ROI。但是他不是矩形。但是我们可以通过下面的按位运算实现:
import cv2 import numpy as np # 加载图像 img1 = cv2.imread('timg.jpg') img2 = cv2.imread('opencv_logo.jpg') # 将logo给加到图片的右上方 这里首先创造出一个ROI rows,cols,channels = img2.shape roi = img1[0:rows, 0:cols] # 在创建一个蒙版的标志,也就是变成灰度图 img2gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY) # mask是根据阈值确定的图片 图片 阈值 背景 ret, mask = cv2.threshold(img2gray, 175, 255, cv2.THRESH_BINARY) # 将msk进行反向处理,0--》1 1--》0 mask_inv = cv2.bitwise_not(mask) # 取 roi 中与 mask 中不为零的值对应的像素的值,其他值为 0 # 注意这里必须有 mask=mask 或者 mask=mask_inv, 其中的 mask= 不能忽略 img1_bg = cv2.bitwise_and(roi,roi,mask = mask) # 将反转后的图片增加到蒙版上 # # 取 roi 中与 mask_inv 中不为零的值对应的像素的值,其他值为 0。 img2_fg = cv2.bitwise_and(img2,img2,mask = mask_inv)# 将有颜色对应的值给挖出来 dst = cv2.add(img1_bg,img2_fg)# 填入黑色的标志中 img1[0:rows, 0:cols ] = dst# 合并给原图 cv2.imshow('img',img1) cv2.waitKey(0) cv2.destroyAllWindows()
程序性能检测及优化
计时API:
import cv2 import numpy as np e1 = cv2.getTickCount() # your code execution e2 = cv2.getTickCount() time = (e2 - e1)/ cv2.getTickFrequency() print(time) 和time作比较: import cv2 import time img1 = cv2.imread('dmn.jpg') e1 = cv2.getTickCount() s = time.time() for i in range(5,49,2): img1 = cv2.medianBlur(img1,i) e2 = cv2.getTickCount() t = (e2 - e1)/cv2.getTickFrequency() print("tc:",t) print("time:",time.time()-s)
OpenCV 中的默认优化:
OpenCV 中的很多函数都被优化过(使用 SSE2,AVX 等)。也包含一些没有被优化的代码。如果我们的系统支持优化的话要尽量利用只一点。在编译时优化是被默认开启的。因此 OpenCV 运行的就是优化后的代码,如果你把优化关闭的话就只能执行低效的代码了。你可以使用函数 cv2.useOptimized()来查看优化是否被开启了,使用函数 cv2.setUseOptimized() 来开启优化。
import cv2 img = cv2.imread('dmn.jpg') import time #优化后中值滤波的速度是原来的两倍。如果你查看源代码的话, # 你会发现中值滤波是被 SIMD 优化的。所以你可以在代码的开始处开启优化 # (你要记住优化是默认开启的)。
print(cv2.useOptimized()) s = time.time() res = cv2.medianBlur(img,49) print(time.time()-s) cv2.setUseOptimized(False) print(cv2.useOptimized()) s = time.time() res = cv2.medianBlur(img,49) print(time.time()-s)
下面看看opencv和numpy和普通算法的区别:
import cv2 import numpy as np img = cv2.imread('dmn.jpg') img = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY) import time s = time.time() # z = cv2.countNonZero(img) print(time.time() - s) s = time.time() z = np.count_nonzero(img) print(time.time() - s)
竟然是第一种写法,它居然比 Nump 快了 20 倍。如果考虑到数组构建的 话,能达到 100 倍的差。
Python 的标量计算比 Nump 的标量计算要快。对于仅包含一两个元素的操作 Python 标量比 Numpy 的数组要快。但是当数组稍微大一点时Numpy 就会胜出了。
效率优化技术
有些技术和编程方法可以让我们最大的发挥 Python 和 Numpy 的威力。我们这里仅仅提一下相关的,你可以通过百度查找更多详细信息。我们要说的最重要的一点是:首先用简单的方式实现你的算法(结果正确最重要),当结果正确后,再使用上面的提到的方法找到程序的瓶颈来优化它。
-
尽量避免使用循环,尤其双层三层循环,它们天生就是非常慢的。
-
算法中尽量使用向量操作,因为 Numpy 和 OpenCV 都对向量操作进行了优化。
-
利用高速缓存一致性。
-
没有必要的话就不要复制数组。使用视图来代替复制。数组复制是非常浪费资源的。
-
就算进行了上述优化,如果你的程序还是很慢,或者说大的训练事件不可避免的话,