一、开始使用图像
原作者:Ana Huamán 兼容性:OpenCV >= 3.4.4
警告:
本教程可能包含过时的信息。
1.1 目标
在本教程中,您将学习如何:
- 从文件中读取图像(使用
cv::imread
) - 在OpenCV窗口中显示图像(使用
cv::imshow
) - 将图像写入文件(使用
cv::imwrite
)
1.2 源代码
下载代码:点击这里
代码一览:
import cv2 as cv
import sys
img = cv.imread("starry_night.jpg")
if img is None:
sys.exit("Could not read the image.")
cv.imshow("Display window", img)
k = cv.waitKey(0)
if k == ord("s"):
cv.imwrite("starry_night.png", img)
1.3 讲解
第一步,导入OpenCV python库。正确的方法是给它另外分配一个名称cv,下面将使用这个名称来引用这个库。
import cv2 as cv
import sys
现在,让我们分析主要代码。作为第一步,我们用OpenCV读取图像“starry_night.jpg”。为此,调用cv::imread函数使用第一个参数指定的文件路径加载图像。第二个参数是可选的,它指定了我们想要的图像格式。这可能是:
- IMREAD_COLOR :以BGR 8位格式加载图像。这是这里使用的 默认 值
- IMREAD_UNCHANGED :按原样加载图像,包括Alpha通道(如果存在)
- IMREAD_GRAYSCALE :将图像作为强度图像加载
读取后,图像数据将存储在cv::Mat对象中。
img = cv.imread("starry_night.jpg")
注意:
OpenCV提供对图像格式Windows位图(bmp)、便携式图像格式(pbm、pgm、ppm)和Sun光栅(sr、ras)的支持。
在插件的帮助下(如果您自己构建库,您需要指定使用它们,但是在我们默认提供的软件包中),您还可以加载JPEG(jpeg,jpg,jpe),JPEG 2000(jp 2-在CMake中代号为Jasper),TIFF文件(tiff,tif)和便携式网络图形(png)等图像格式。
此外,OpenEXR也是一种可能性。
然后,如果图像被正确加载,则执行检查。
if img is None:
sys.exit("Could not read the image.")
然后,通过调用cv::imshow函数显示图像。第一个参数是窗口的标题,第二个参数是将要显示的cv::Mat对象。
因为我们希望我们的窗口一直显示到用户按下一个键(否则程序会很快结束),所以我们使用cv::waitKey函数,它唯一的参数是它应该等待用户输入多长时间(以毫秒为单位)。零意味着永远等待。返回值是按下的键。
cv.imshow("Display window", img)
k = cv.waitKey(0)
最后,如果按下的键是“s”键,则图像被写入.png文件。为此,调用cv::imwrite函数,该函数将文件路径和cv::Mat对象作为参数。
if k == ord("s"):
cv.imwrite("starry_night.png", img)
二、开始使用视频
2.1 目标
- 了解如何读取视频、显示视频和保存视频。
- 学习从摄像头捕获视频并显示它。
- 您将学习这些函数:
cv.VideoCapture()
,cv.VideoWriter()
2.2 从摄像头捕获视频
下载代码:点击这里
通常,我们必须用相机捕捉实时流。OpenCV提供了一个非常简单的接口来实现这一点。让我们从摄像头捕捉视频(我使用笔记本电脑上的内置网络摄像头),将其转换为灰度视频并显示。这只是一个简单的任务。
要捕获视频,您需要创建一个 VideoCapture 对象。它的参数可以是设备索引或视频文件的名称。设备索引只是指定哪个相机的数字。通常会连接一台相机(就像我的情况一样)。所以我只传递0(或-1)。您可以通过传递1等来选择第二个相机。
之后,您可以逐帧捕获。但在最后,不要忘记释放捕获。
import numpy as np
import cv2 as cv
cap = cv.VideoCapture(0)
if not cap.isOpened():
print("无法打开摄像头")
exit()
while True:
# 逐帧捕获
ret, frame = cap.read()
# 如果正确捕获,ret为True
if not ret:
print("Can't receive frame (stream end?). Exiting ...")
break
# 操作来到这里
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
# 显示捕获结果
cv.imshow('frame', gray)
if cv.waitKey(1) == ord('q'):
break
# 当所有事情做完时,释放捕获
cap.release()
cv.destroyAllWindows()
cap.read()
返回一个bool(True
/False
)。如果帧读取正确,则为True
。因此,您可以通过检查此返回值来检查视频的结尾。
有时,cap可能没有初始化捕获。在这种情况下,此代码显示错误。您可以通过方法 cap.isOpened() 检查它是否已初始化。如果是True,那就可以。否则改为使用 cap.open() 打开它。
您还可以使用 cap.get(propId) 方法访问此视频的一些功能,其中propId是从0到18的数字。每个数字表示视频的属性(如果它适用于该视频)。完整的细节可以在这里看到:cv::VideoCapture::get()。其中一些值可以使用 cap.set(propId,value) 修改。值是您想要的新值。
例如,我可以通过cap.get(cv.CAP_PROP_FRAME_WIDTH)
和cap.get(cv.CAP_PROP_FRAME_HEIGHT)
检查框架的宽度和高度。它默认为640x480。但我想把它修改成320x240。使用ret = cap.set(cv.CAP_PROP_FRAME_WIDTH, 320)
和ret = cap.set(cv.CAP_PROP_FRAME_HEIGHT, 240)
。
注意:
如果你得到错误,确保你的相机在使用任何其他相机应用程序(如Linux的Cheese)时是正常工作。
2.3 从文件播放视频
下载代码(视频也在其中):点击这里
从文件中播放视频与从相机中捕获视频相同,只需将相机索引更改为视频文件名。此外,在显示帧时,为cv.waitKey()
使用适当的时间。如果它太小,视频将非常快,如果它太高,视频将很慢(好吧,这就是如何以慢动作显示视频的方法)。正常情况下25毫秒就可以了。
import numpy as np
import cv2 as cv
cap = cv.VideoCapture('vtest.avi')
while cap.isOpened():
ret, frame = cap.read()
# 如果正确捕获,ret为True
if not ret:
print("无法捕获帧(播放结束?)正在退出...")
break
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
cv.imshow('frame', gray)
if cv.waitKey(1) == ord('q'):
break
cap.release()
cv.destroyAllWindows()
注意:
确保安装了正确版本的ffmpeg或gstreamer。有时使用视频捕获是一件令人头痛的事情,主要是由于错误安装ffmpeg/gstreamer。
2.4 保存视频
下载代码:点击这里
我们捕获一个视频,并逐帧处理它,想保存这个视频。对于图像,它非常简单:只需使用cv.imwrite()
。在这里,需要多做一点工作。
这一次我们创建一个 VideoWriter 对象。我们应该指定输出文件名(例如:output.avi)。然后我们应该指定 FourCC 代码(细节在下一段中)。然后应该传递每秒帧数(fps)和帧大小。最后一个是isColor标志。如果它是 True ,编码器期望彩色帧,否则它与灰度帧一起工作。
FourCC是用于指定视频编解码器的4字节代码。可用代码的列表可以在fourcc.org中找到。它依赖于平台。下面的解码器对我来说很好用。
- 在Fedora中:DIVX,XVID,MJPG,X264,WMV1,WMV2. (XVID更优选。MJPG会产生大尺寸视频。X264提供非常小尺寸的视频)
- 在Windows中:DIVX(更多内容有待测试和添加)
- 在OSX中:MJPG(.mp4),DIVX(.avi),X264(.mpeg)。
对于MJPG,FourCC代码作为cv.VideoWriter_fourcc('M', 'J', 'P', 'G')
orcv.VideoWriter_fourcc('MJPG')
传递。
下面的代码从摄像头捕获,在垂直方向上翻转每一帧,并保存视频。
import numpy as np
import cv2 as cv
cap = cv.VideoCapture(0)
# 定义编解码器并创建VideoWriter对象
fourcc = cv.VideoWriter_fourcc(*'XVID')
out = cv.VideoWriter('output.avi', fourcc, 20.0, (640, 480))
while cap.isOpened():
ret, frame = cap.read()
if not ret:
print("无法捕获帧(播放结束?)正在退出...")
break
frame = cv.flip(frame, 0)
# 写入翻转的帧
out.write(frame)
cv.imshow('frame', frame)
if cv.waitKey(1) == ord('q'):
break
# 如果工作完成,释放所有内容内容
cap.release()
out.release()
cv.destroyAllWindows()
三、OpenCV中的绘图函数
3.1 目标
- 学习使用OpenCV绘制不同的几何形状。
- 您将学习这些函数:cv.line()、cv.circle()、cv.rectangle()、cv.ellipse()、cv.putText()等。
3.2 代码
在所有上述函数中,您将看到下面给出的一些常见参数:
- img:您要绘制形状的图像。
- color:形状的颜色。对于BGR,将其作为元组传递,例如:(255,0,0)表示蓝色。对于灰度,只需传递标量值。
- thickness:线或圆等的厚度。如果为闭合图形(如圆)传递-1,它将填充形状。默认厚度 = 1。
- lineType:线的类型,是否为8连、抗锯齿线等,默认为8连。
cv.LINE_AA
提供了抗锯齿的线条,看起来非常适合曲线。
3.3 画线
要绘制一条直线,您需要传递直线的起始和结束坐标。我们将创建一个黑色图像,并在其上从左上角到右下角绘制一条蓝线。
import numpy as np
import cv2 as cv
# 创建一个黑色背景
img = np.zeros((512, 512, 3), np.uint8)
# 画一条5像素粗的蓝色斜线
cv.line(img, (0, 0), (511, 511), (255, 0, 0), 5)
3.4 绘制矩形
要绘制矩形,需要矩形的左上角和右下角。这次我们将在图像的右上角绘制一个绿色矩形。
# 画一条3像素粗的绿色空心矩形
cv.rectangle(img, (384, 0), (510, 128), (0, 255, 0), 3)
3.5 画圆
要画一个圆,你需要它的圆心坐标和半径。我们将在上面画的矩形内画一个圆。
# 画一个红色的实心圆
cv.circle(img, (447, 63), 63, (0, 0, 255), -1)
3.6 绘制椭圆
要绘制椭圆,我们需要传递几个参数。一个参数是中心位置(x,y)。下一个参数是轴的长度(长轴, 短轴)。角度是椭圆逆时针方向旋转的角度。
startAngle和endAngle表示从长轴顺时针方向测量的椭圆弧的起点和终点。即,给出值0和360给出完整的椭圆。有关详细信息,请查看cv.ellipse()
的文档。下面的例子在图像的中心绘制了一个半椭圆。
# 画一个蓝色椭圆
cv.ellipse(img, (256, 256), (100, 50), 0, 0, 180, 255, -1)
3.7 绘制多边形
要绘制多边形,首先需要顶点的坐标。将这些点放入一个ROWSx1x2的数组中,其中ROWS是顶点数,类型应为int32。在这里,我们画一个小多边形的四个黄色顶点。
# 绘制一个黄色四边形
pts = np.array([[10, 5], [20, 30], [70, 20], [50, 10]], np.int32)
pts = pts.reshape((-1, 1, 2))
cv.polylines(img, [pts], True, (0, 255, 255))
注意:
如果第三个参数为False,你将得到一条连接所有点的折线,而不是一个封闭的形状。
cv.polylines()
可用于绘制多条线。只需创建一个包含所有要绘制的线条的列表,并将其传递给函数。所有线条将单独绘制。这是一种比为每一行调用cv.line()
更好更快的绘制一组直线的方法。
3.8 将文本添加到图像
要在图像中放置文本,您需要指定以下内容。
- 要写入的文本数据
- 您想要放置它的位置坐标(即数据开始的左下角)。
- 字体类型(查看
cv.putText()
文档以获得支持的字体) - 字体比例(指定字体大小)
- 常规的东西,如颜色,厚度,线型等。为了更好的外观,建议使用
lineType = cv.LINE_AA
。
我们将在图像上以白色颜色写入 OpenCV 。
# 写入白色文字OpenCV
font = cv.FONT_HERSHEY_SIMPLEX
cv.putText(img, 'OpenCV', (10, 500), font, 4, (255, 255, 255), 2, cv.LINE_AA)
3.9 结果
现在是时候看看我们的最终结果了。正如您在前几篇文章中学习的那样,显示图像以查看它。
完整代码下载:点击这里
四、鼠标当画笔
4.1 目标
- 学习在OpenCV中处理鼠标事件
- 您将学习这些函数:cv.setMouseCallback()
4.2 简单演示
下载代码:点击这里
在这里,我们创建了一个简单的应用程序,它可以在双击图像的任何地方画一个圆。
首先,我们创建一个鼠标回调函数,当鼠标事件发生时执行。鼠标事件可以是任何与鼠标相关的事件,如左键按下,左键向上,左键双击等,它为我们提供了每个鼠标事件的坐标(x, y)。
有了事件和位置,我们可以做任何事情。要列出所有可用的事件,请在Python终端中运行以下代码:
import cv2 as cv
events = [i for i in dir(cv) if 'EVENT' in i]
print(event)
创建鼠标回调函数有一个特定的格式,在任何地方都是一样的。不同的只是函数的功能。因此,我们的鼠标回调函数只做一件事,就是在我们双击的地方画一个圆。请看下面的代码,在注释中不言自明:
import numpy as np
import cv2 as cv
# 鼠标回调函数
def draw_circle(event,x,y,flags,param):
if event == cv.EVENT_LBUTTONDBLCLK:
cv.circle(img,(x,y),100,(255,0,0),-1)
# 创建黑色背景和窗口,将函数绑定到窗口
img = np.zeros((512,512,3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image',draw_circle)
while(1):
cv.imshow('image',img)
if cv.waitKey(20) & 0xFF == 27:
break
cv.destroyAllWindows()
4.3 更高级的演示
下载代码:点击这里
现在,我们将使用一个更好的应用程序。在这个程序中,我们通过拖动鼠标来绘制矩形或圆形(取决于我们选择的模式),就像在画图程序中一样。因此,我们的鼠标回调函数分为两部分,一部分用于绘制矩形,另一部分用于绘制圆形。
这个具体示例将有助于创建和理解一些交互式应用程序,如对象跟踪、图像分割等。
import numpy as np
import cv2 as cv
drawing = False # 如果按下鼠标则为True
mode = True # 如果为True,绘制矩形。按‘m’切换到曲线
ix, iy = -1, -1
# 鼠标回调函数
def draw_circle(event, x, y, flags, param):
global ix, iy, drawing, mode
if event == cv.EVENT_LBUTTONDOWN:
drawing = True
ix, iy = x, y
elif event == cv.EVENT_MOUSEMOVE:
if drawing == True:
if mode == True:
cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), -1)
else:
cv.circle(img, (x, y), 5, (0, 0, 255), -1)
elif event == cv.EVENT_LBUTTONUP:
drawing = False
if mode == True:
cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), -1)
else:
cv.circle(img, (x, y), 5, (0, 0, 255), -1)
接下来,我们必须将这个鼠标回调函数绑定到OpenCV窗口。在主循环中,我们应该为键'm'设置一个键盘绑定,以在矩形和圆形之间切换。
img = np.zeros((512, 512, 3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image', draw_circle)
while (1):
cv.imshow('image', img)
k = cv.waitKey(1) & 0xFF
if k == ord('m'):
mode = not mode
elif k == 27:
break
cv.destroyAllWindows()
4.4 额外资源
4.4.1 练习
- 在上一个例子中,我们画了一个填充矩形。修改代码以绘制一个未填充的矩形。
五、使用Trackbar颜色栏
5.1 目标
- 学习将Trackbar绑定到OpenCV窗口
- 您将学习这些函数:cv.getTrackbarPos(),cv. getTrackbar()等。
5.2 代码示例
下载代码:点击这里
在这里,我们将创建一个简单的应用程序,显示您指定的颜色。你有一个显示颜色的窗口和三个轨迹条来指定B,G,R颜色。你滑动轨迹条,窗口的颜色也会相应地改变。
默认情况下,初始颜色将设置为黑色。
对于cv.createTrackbar()
函数,第一个参数是轨迹条名称,第二个参数是它所附加的窗口名称,第三个参数是默认值,第四个参数是最大值,第五个参数是每次轨迹条值更改时执行的回调函数。
回调函数总是有一个默认参数,即轨迹条位置。在我们的例子中,函数什么也不做,所以我们简单地传递。
Trackbar的另一个重要应用是将其用作按钮或开关。默认情况下,OpenCV没有按钮功能。所以你可以使用Trackbar来获得这样的功能。
在我们的应用程序中,我们创建了一个开关,只有当开关打开时,应用程序才能工作,否则屏幕总是黑色的。
import numpy as np
import cv2 as cv
def nothing(x):
pass
# 创建黑色背景和窗口
img = np.zeros((300, 512, 3), np.uint8)
cv.namedWindow('image')
# 为变换颜色创建Trackbar
cv.createTrackbar('R', 'image', 0, 255, nothing)
cv.createTrackbar('G', 'image', 0, 255, nothing)
cv.createTrackbar('B', 'image', 0, 255, nothing)
# 为开关功能创建一个开关
switch = '0 : OFF \n1 : ON'
cv.createTrackbar(switch, 'image', 0, 1, nothing)
while (1):
cv.imshow('image', img)
k = cv.waitKey(1) & 0xFF
if k == 27:
break
# 获取4个Trackbar的位置
r = cv.getTrackbarPos('R','image')
g = cv.getTrackbarPos('G','image')
b = cv.getTrackbarPos('B','image')
s = cv.getTrackbarPos(switch,'image')
if s == 0:
img[:] = 0
else:
img[:] = [b, g, r]
cv.destroyAllWindows()
应用程序的屏幕截图如下所示:
5.3 额外资源
5.3.1 练习
- 使用轨迹条创建具有可调颜色和画笔半径的Paint应用程序。对于绘图,请参考以前的教程。