Gui Features in OpenCV

1 Getting Started with Images

1.1 目标

  • 学习如何读取图像、展示图像、保存图像
  • 学习函数:cv2.imread(), cv2.imshow(), cv2.imwrite()
  • 学习用 matplotlib 展示图像

1.2 使用OpenCV

1.2.1 读取图像

函数 cv2.imread() 有两个参数:

  • 图像路径
  • 读取图像的方式:
    • cv2.IMREAD_COLOR: 加载色彩图像,默认flag;
    • cv2.IMREAD_GRAYSCALE: 以灰度模式(grayscale mode)加载图像;
    • cv2.IMREAD_UNCHANGED: 加载包含alpha通道(alpha channel)的图像.

注意:以上三种状态可分别用整数 1,0 和 -1表示。

import numpy as np
import cv2

# Load an color image in grayscale
img = cv2.imread('messi5.jpg', 0)
print(img)

警告: 即使图像路径是错误的,它也不会报错,但是 print(img) 会给出 None.

1.2.2 展示图像

用 cv2.imshow() 在窗口中展示图像,窗口将自动适应图像大小。
参数:

  • 一个字符串,表示窗口名称;
  • 读取的图像名,可以创建多个有着不同窗口名称的窗口。
cv2.imshow('image', img)
cv2.waitKey(0)  # 将鼠标放在图像上,然后按任意键才会停止
cv2.destroyAllWindows()

cv2.waitKey()是一个键盘绑定函数,它的参数是以毫秒(milliseconds)记的时间。这个函数对于任意键盘事件等待特定的毫秒数。如果你在那个时间按下任意键,程序继续。如果参数为0,它将无限期的等待键盘输入。它也可以设置为检测专门的键盘输入,例如键a是否被按下,这个下面将会讨论。

cv2.destroyAllWindows()销毁我们创建的所有窗口。如果你想销毁任意指定的窗口,传递给函数cv2.destroyWindow()精确的窗口名作为参数。

注意:有一种特定的情况,在这种情况下你已经创建了窗口,并且加载了图像。你可以指定窗口尺寸是否可以调整,这时候可以用cv2.namedWindow()函数。默认flag是cv2.WINDOW_AUTOSIZE,但是如果你指定flag是cv2.WINDOW_NORMAL,你可以调整窗口尺寸。当图像维度太大和向窗口增加轨迹条时非常有用。

cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

1.2.3 保存图像

cv2.imwrite() 第一个参数:文件名,第二个参数:保存的图像。

cv2.imwrite('messigray.png', img)

1.2.4 总结

下面的程序演示:加载灰色图像,展示,当按下 's' 键时保存图像并退出,或者按 ESC 键不保存且退出。

import numpy as np
import cv2

img = cv2.imread('messi5.jpg', 0)
cv2.imshow('image', img)
k = cv2.waitKey(0)
if k == 27:      # wait for ESC key to exit
    cv2.destroyAllWindows()
elif k == ord('s'):   # wait for 's' key to save and exit
    cv2.imwrite('messigray2.png', img)
    cv2.destroyAllWindows()

ord()以一个字符作为参数,返回对应的ASCII数值,或者Unicode数值,如果所给的Unicode字符超出了你的Python定义范围,则会引发一个TypeError的异常。
如果使用的是64位机器,需要修改 k = cv2.waitKey(0) 为 k = cv2.waitKey(0) & 0xFF

1.3 使用Matplotlib

import numpy as np
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('messi5.jpg', 0)
plt.imshow(img, cmap='gray', interpolation = 'bicubic')
plt.xticks([])
plt.yticks([])     # to hide tick values on X and Y axis
plt.show() 

警告:OpenCV中加载颜色图像是 BGR 模式,而 Matplotlib 中展示图像是 RGB 模式。所以颜色图像用OpenCV读取,在Matplotlib中不会被正确展示。

下面的例子来对比OpenCV和Matplotlib中的图像显示。

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('messi5.jpg')
b,g,r = cv2.split(img)
img2 = cv2.merge([r,g,b])

plt.rcParams['figure.figsize'] = (12, 9)
plt.subplot(121)
plt.imshow(img) # expects distorted color
plt.subplot(122)
plt.imshow(img2) # expect true color
plt.show()

cv2.imshow('bgr image',img) # expects true color
cv2.imshow('rgb image',img2) # expects distorted color
cv2.waitKey(0)
cv2.destroyAllWindows()

# 可以这样转换
img = cv2.imread('messi5.jpg')
img2 = img[:,:,::-1]

plt.rcParams['figure.figsize'] = (12, 9)
plt.subplot(121)
plt.imshow(img) # expects distorted color
plt.subplot(122)
plt.imshow(img2) # expect true color
plt.show()

2 Getting Started with Videos

2.1 目标

  • 学习读取视频,展示视频,保存视频
  • 学习从摄像头中抓取视频,并且展示
  • 学习函数 cv2.VideoCapture(), cv2.VideoWriter()

2.2 用摄像头抓取视频

我们经常要用摄像头抓取实时视频(live stream),OpenCV 对此提供了一个简单接口。让我们从摄像头中抓取一个视频,转化为灰度(grayscale)视频并展示。从一个简单的任务开始。

抓取视频,首先要创建一个 VideoCapture 对象。它的参数可以是设备索引或者视频文件的名称。设备索引是一个确定摄像头的编号。正常情况下摄像头会被连接,所以我简单的传递0(或者-1)。你也可以通过传递1来选择第二个摄像头。之后,你可以一帧帧( frame-by-frame) 抓取。但是,最后不要忘记释放抓取。

import numpy as np
import cv2

cap = cv2.VideoCapture(0)  # 启动电脑自带的摄像头

while(True):
    # Capture frame-by-frame
    ret, frame = cap.read()
    
    # Our operations on the frame come here
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    # Display the resulting frame
    cv2.imshow('frame', gray)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
        
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()

上面的程序是一帧一帧抓取图像,然后用cv2.cvtColor()将其转化成灰色,然后展示出来。

cap.read()返回bool类型。如果读取frame正确,返回True。所以你可以通过检查返回值来检查视频的结尾。

有时,cap没有初始化,这种情况下代码会显示错误。你可以通过方法cap.isOpened()来检查cap是否被初始化。如果初始化了,那没问题,否则,用cap.open()打开它。

你也可以用cap.get(propld)方法来访问视频的某些特征,propld是0到18的数字。每个数字表示视频的一个属性(如果对视频有意义),并且可以在Property Identifier看到整个细节。其中有一些数值可以用cap.set(propld, value)修改。

例如,我可以通过cap.get(3)和cap.get(4)来检查帧(frame)的宽度(width)和高度(height),默认640x480。但是如果我想修改为320x240,只要用 ret = cap.set(3, 320) 和 ret = cap.set(4, 240)即可。

2.3 从文件中播放视频

可以不启用摄像头而从文件中播放视频,恰当利用 cv2.waitKey()的时间,如果太小,视频将播放的非常快,如果太大,视频会播放的非常慢(慢动作展示视频)。在正常的情况下 25 milliseconds 比较合适。

import numpy as np
import cv2

cap = cv2.VideoCapture('vtest.mp4')

while(cap.isOpened()):
    ret, frame = cap.read()
    # 这里保证获取到图像,而且必须加上,可以作为读取结束的判断,否则播放到最后一帧会出错
    if ret == True:  
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        cv2.imshow('frame', gray)
        
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break
    else:
        break

cap.release()
cv2.destroyAllWindows()

上面的程序在windows系统下运行正常,但在Mac OSX 10.10.3中会出错,猜测是没有安装ffmpeg or gstreamer的原因(没有适合系统版本的),暂未找到解决方法。

2.4 保存视频

如果我们要保存图像,用函数 cv2.imwrite()即可,但在保存视频的时候,稍微麻烦一点。

首先要创建一个 VideoWriter 对象。我们应该指定输出文件的名字(例如:output.avi),然后指定 FourCC 编码,然后是应该传递的每秒帧数和帧的尺寸,最后是 isColor 标识。如果 isColor == True, 编码器会编码成color frame,否则是是grayscale frame。

FourCC是一个用于指定视频编码器(codec)的4字节代码,有效代码列表在fourcc.org中,它是平台依赖的,下面是编码器:

  • In Fedora: DIVX, XVID, MJPG, X264, WMV1, WMV2. (XVID is more preferable. MJPG results in high size video. X264 gives very small size video)
  • In Windows: DIVX (More to be tested and added)
  • In OSX : (I don’t have access to OSX. Can some one fill this

FourCC 代码被传递为 cv2.VideoWriter_fourcc('M','J','P','G') 或者 cv2.VideoWriter_fourcc(*'MJPG) for MJPG.

下面的代码从摄像头中抓取,在垂直方向flip每一帧并且保存。

import numpy as np
import cv2

cap = cv2.VideoCapture(0)

# 获取视频播放界面长宽
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH) + 0.5)
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) + 0.5)

# Define the codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter('output2.mp4',fourcc, 20.0, (width, height))
# out = cv2.VideoWriter('output3.mp4',fourcc, 20.0, (640, 480))

while(cap.isOpened()):
    ret, frame = cap.read()
    if ret==True:
        frame = cv2.flip(frame,0) 

        # write the flipped frame
        out.write(frame)

        cv2.imshow('frame',frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break

# Release everything if job is finished
cap.release()
out.release()
cv2.destroyAllWindows()

cv2.flip() 1:水平翻转,0:垂直翻转,-1:水平垂直翻转。

保存视频不成功,应该是fourcc = cv2.VideoWriter_fourcc(*'mp4v')编码方式的问题,对Mac OSX不友好。
在利用了width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH) + 0.5) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) + 0.5),并且把编码方式修改为'mp4v',输出视频格式修改为.mp4之后就正常了,其他方式不起作用。

3 Drawing Functions in OpenCV

3.1 目标

  • 学习利用OpenCV绘制不同的几何形状
  • 学习函数 cv2.line(), cv2.circle(), cv2.rectangle(), cv2.ellipse(), cv2.putText()等

3.2 代码

上面所有的函数中,有一些通用参数,如下所示:

  • img: 你想要在其上画出形状的图像
  • color: 形状的颜色。对BGR而言,作为元组传递,例如(255,0,0)是蓝色。对于grayscale而言,仅仅传递标量值
  • thickness: 线和圆圈等的粗细。如果-1被传递给像圆圈一样的封闭图形,将会填充形状。默认thickness = 1
  • lineType: 线的类型,8连接,抗锯齿线等。默认8连接。cv2.LINE_AA给出的抗锯齿线看起来非常平滑。

3.2.1 绘制直线

画一条线,需要给出线的开始和结束坐标。我们将创建一个黑色图像,并且在其上从左上角到右下角画一条蓝色的线。

import numpy as np
import cv2

# Create a black image
img = np.zeros((512, 512, 3), np.uint8)

# Drawing a diagonal blue line with thickness of 5 px
img = cv2.line(img, (0,0), (511,511), (255,0,0), 5)

cv2.imshow('image', img)
cv2.waitKey(0)  # 将鼠标放在图像上,然后按任意键才会停止
cv2.destroyAllWindows()

3.2.2 绘制矩形

画矩形,需要矩形的左上角和右下角坐标,我们将要在图像的右上角画一个绿色的矩形。

img = cv2.rectangle(img, (384,0), (510,128), (0,255,0), 3)

cv2.imshow('image', img)
cv2.waitKey(0)  # 将鼠标放在图像上,然后按任意键才会停止
cv2.destroyAllWindows()

3.2.3 绘制圆

画圆,需要中心坐标和半径。我们将要在上面的矩形中画一个圆。

img = cv2.circle(img, (447,63), 63, (0,0,225), -1)

cv2.imshow('image', img)
cv2.waitKey(0)  # 将鼠标放在图像上,然后按任意键才会停止
cv2.destroyAllWindows()

3.2.4 绘制椭圆

画椭圆,需要传递几个参数。1.中心位置,2.轴长度(主轴长,次轴长),3.angle:逆时针方向的椭圆旋转角度,4.startAngle和endAngle:从主轴顺时针方向开始测量的椭圆弧度的开始和结束角度,例如,0和360代表整个椭圆。对于更多的细节可以查询cv2.ellipse()的文件。下面的例子在图像的中心位置绘制了半个椭圆。

img = cv2.ellipse(img, (256,256), (100,50), 0, 0, 180, 255, -1)

cv2.imshow('image', img)
cv2.waitKey(0)  # 将鼠标放在图像上,然后按任意键才会停止
cv2.destroyAllWindows()

3.2.5 绘制多边形

绘制多边形,首先需要定点坐标。使这些点组成一个形如ROWS×1×2的数组,其中ROWS是顶点的数量,并且必须是int32。这里用黄色绘制四个顶点的小多边形。

pts = np.array([[10,5], [20,30], [70,20], [50,10]], np.int32)
pts = pts.reshape((-1, 1, 2))
img = cv2.polylines(img, [pts], True, (0,255,255))

cv2.imshow('image', img)
cv2.waitKey(0)  # 将鼠标放在图像上,然后按任意键才会停止
cv2.destroyAllWindows()

如果第三个参数是False,将会得到一个连接所有点的多边线,而不是闭合的形状。

cv2.polylines()可以来绘制多条线。创建一个想要绘制的所有线的列表,然后将其传递给函数。用这种方法绘制一组线相比调用cv2.line()更好更快。

3.2.6 在图像上添加文本

在图像中放置文本,需要指定下面的事情:

  • 文本数据
  • 放置文本的位置坐标(左上角是数据开始的位置)
  • 字体类型(通过查询cv2.putText()文档来查看支持的字体)
  • 字体刻度(指定字体的大小)
  • 通常的事情如颜色、粗细、线型等。为了更好的显示,推荐lineType=cv2.LINE_AA.

我们将在图像中用白色写上“OpenCV”:

font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, 'OpenCV', (10,500), font, 4, (255,255,255), 2, cv2.LINE_AA)

cv2.imshow('image', img)
cv2.waitKey(0)  # 将鼠标放在图像上,然后按任意键才会停止
cv2.destroyAllWindows()

3.3 更多资源

在椭圆函数中用的角度不是我们的环形角度,更多细节访问这个讨论

3.4 练习

试着利用OpenCV中的绘图函数创建一个OpenCV logo.

4 Mouse as a Paint-Brush

4.1 目标

  • 学习在OpenCV中处理鼠标事件
  • 学习函数cv2.setMouseCallback()

4.2 简单演示

我们创建一个简单的应用,可以在图像上双击过的地方绘制圆圈。

首先我们创建一个当鼠标事件发生时被执行的鼠标回调函数。鼠标事件可以是和鼠标相关的任何事情,比如左键按下,左键松开,左键双击。对于每个鼠标事件,会给我们一个坐标(x,y)。有了这个事件和位置,我们可以做任何喜欢的事。列出所有有效地事件,可以在Python终端运行下面的代码:

import cv2

events = [i for i in dir(cv2) if 'EVENT' in i]
print(events)

创建鼠标回调函数有一个专门的格式,不同的地方仅仅是函数的功能。所以我们的鼠标回调函数做一件事情,它就在我们双击的地方绘制一个圆圈。

import cv2
import numpy as np

# mouse callback function
def draw_circle(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONDOWN:
        cv2.circle(img, (x,y), 100, (255,0,0), -1)
        
# Create a black image, a window and bind the function to window
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()

将 event 为 cv2.EVENT_LBUTTONDBLCLK 修改为 cv2.EVENT_LBUTTONDOWN,才有反应。为什么?而在windows环境下不需要修改就可以成功。

4.3 更多高级演示

我们将要做更好的应用。在这个应用当中,我们通过像绘图应用中拖拽鼠标那样绘制矩形或者圆形(依赖于我们选择的模式)。所以我们的鼠标回调函数有两部分,一部分绘制矩形,一部分绘制圆形。这个例子将会在创建和理解许多交互式应用——如对象跟踪、图像分割——中等很有帮助。

import cv2
import numpy as np

drawing = False  # true if mouse is pressed
mode = True  # if True, draw rectangle. Press 'm' to toggle to curve
ix, iy = -1, -1

# mouse callback function
def draw_circle(event, x, y, flags, param):
    global ix, iy, drawing, mode
    
    if event == cv2.EVENT_LBUTTONDOWN:
        drawing = True
        ix, iy = x, y
    
    elif event == cv2.EVENT_MOUSEMOVE:
        if drawing == True:
            if mode == True:
                cv2.rectangle(img, (ix,iy), (x,y), (0,255,0), -1)
            else:
                cv2.circle(img, (x,y), 5, (0,0,255), -1)
                
    elif event == cv2.EVENT_LBUTTONUP:
        drawing = False
        if mode == True:
            cv2.rectangle(img, (ix,iy), (x,y), (0,255,0), -1)
        else:
            cv2.circle(img, (x,y), 5, (0,0,255), -1)

下面我们必须将这个鼠标回调函数和OpenCV窗口绑定。在主循环中,我们应该对‘m’键设置键盘绑定用于在矩形和圆形之间转换。

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 == ord('m'):
        mode = not mode
    elif k == 27:
        break
        
cv2.destroyAllWindows()

回调函数是如何执行的?循环中没有这个函数呀。

5 Trackbar as the Color Ralette

5.1 目标

学习将滑动条绑定到OpenCV窗口
学习函数cv2.getTrackbarPos(), cv2.createTrackbar()等

5.2 代码演示

我们将在这里创建一个简单的应用,可以展示你指定的颜色。你有一个展示颜色和指定B,G,R颜色的三个滑动条的窗口。滑动相应的滑动条,相应的,窗口颜色改变。默认,初始颜色被设为黑色。

cv2.createTrackbar(trackbarName, windowName, value, count, onChange)
参数:

  • trackbar name
  • window name to which it is attached
  • default value
  • maximum value
  • callback function which is executed everytime trackbar value changes

回调函数有一个叫trackbar position的默认参数。在我们的例子中,这个函数不做任何事情,我们简单的pass掉就可以了。

trackbar的另一个重要应用是将它作为一个button或者switch。默认,OpenCV没有button功能。所以我们可以用trackbar来实现这个功能。在我们的应用中,我们已经创建了一个switch,只有当switch是开启的时候应用才会工作,否则屏幕是黑色的。

import cv2
import numpy as np

def nothing(x):
    pass

# Create a black image, a window
img = np.zeros((300, 512, 3), np.uint8)
cv2.namedWindow('image')

# create trackbars for color change
cv2.createTrackbar('R', 'image', 0, 255, nothing)
cv2.createTrackbar('G', 'image', 0, 255, nothing)
cv2.createTrackbar('B', 'image', 0, 255, nothing)

# create switch for ON/OFF functionality
switch = '0: OFF...1: ON'
cv2.createTrackbar(switch, 'image', 0, 1, nothing)

while(1):
    cv2.imshow('image', img)
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
        
    # get current position of four trackbars
    r = cv2.getTrackbarPos('R', 'image')
    g = cv2.getTrackbarPos('G', 'image')
    b = cv2.getTrackbarPos('B', 'image')
    s = cv2.getTrackbarPos(switch, 'image')
    
    if s == 0:
        img[:] = 0
    else:
        img[:] = [b,g,r]
        
cv2.destroyAllWindows()

注意: Mac OSX 中运行失败,在windows下运行成功。

posted @ 2018-06-30 20:53  Hiidiot  阅读(390)  评论(0编辑  收藏  举报