OpenCV-python视频入门

Posted 轻窕

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV-python视频入门相关的知识,希望对你有一定的参考价值。

目标

  • 学习阅读视频、显示视频和保存视频。
  • 学习从相机捕捉视频并显示它。
  • 您将学习这些函数​ :
    cv.VideoCapture()从视频文件、图像序列或相机中捕获视频的类
    cv.VideoWriter()捕获一个视频并逐帧处理它,保存想要的视频

从相机捕捉视频

通常,我们必须用相机捕捉实时流。OpenCV 提供了一个非常简单的接口来执行此操作。让我们从相机中捕捉视频(我使用笔记本电脑上的内置网络摄像头),将其转换为灰度视频并显示。只需一个简单的任务即可开始。

要捕获视频,您需要创建一个VideoCapture对象。它的参数可以是设备索引或视频文件的名称。设备索引只是指定哪个相机的数字。通常会连接一台相机(如我的情况)。所以我只是传递0(或-1)。您可以通过传递 1 来选择第二个相机,依此类推。之后,您可以逐帧捕获。但最后,不要忘记释放捕获。

源代码

  • Code at glance:
    import numpy as np
    import cv2 as cv
    cap = cv.VideoCapture(0)
    if not cap.isOpened():
        print("Cannot open camera")
        exit()
    while True:
        # Capture frame-by-frame(逐帧捕捉)
        ret, frame = cap.read()
        # if frame is read correctly ret is True(如果帧被正确读取 ret 为 Tru)
        if not ret:
            print("Can't receive frame (stream end?). Exiting ...")
            break
        # Our operations on the frame come here(我们对帧的操作到这里)
        gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
        # Display the resulting frame(显示结果帧)
        cv.imshow('frame', gray)
        if cv.waitKey(1) == ord('q'):
            break
    # When everything done, release the capture(一切完成后,释放捕获)
    cap.release()
    cv.destroyAllWindows()
  • Result at glance:

 解释

cap.read()返回一个布尔值TrueFalse)。如果帧被正确读取,它将是True. 因此,您可以通过检查此返回值来检查视频的结尾。

有时,cap 可能没有初始化捕获。在这种情况下,此代码显示错误。您可以通过cap.isOpened()方法检查它是否已初始化。如果是True,好的。否则使cap.open()打开它。

您还可以使用cap.get(propId)方法访问此视频的某些功能,其中 propId 是从 0 到 18 的数字。每个数字表示视频的一个属性(如果它适用于该视频)。完整的细节可以在这里看到cv::VideoCapture::get()。其中一些值可以使用cap.set(propId, value)进行修改。值是您想要的新值。

例如,我可以通过和检查框架的宽度和高度。默认情况下它给我 640x480。但我想将其修改为 320x240。只需使用cap.get(cv.CAP_PROP_FRAME_WIDTH)cap.get(cv.CAP_PROP_FRAME_HEIGHT)
ret = cap.set(cv.CAP_PROP_FRAME_WIDTH,320)ret=cap.set(cv.CAP_PROP_FRAME_HEIGHT,240)

笔记

如果您遇到错误,请确保您的相机在使用任何其他相机应用程序(如 Linux 中的 Cheese)时工作正常。

从文件播放视频

从文件播放视频与从相机捕获视频相同,只需将相机索引更改为视频文件名即可。此外,在显示框架时,请使用适当的时cv.waitKey()。如果太少,视频会很快,如果太高,视频会很慢(嗯,这就是您可以慢动作显示视频的方式)。在正常情况下,25 毫秒就可以了。

import numpy as np
import cv2 as cv
cap = cv.VideoCapture('vtest.avi')
while cap.isOpened():
    ret, frame = cap.read()
    # if frame is read correctly ret is True(如果帧被正确读取 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()

笔记

确保安装了正确版本的 ffmpeg 或 gstreamer。有时使用视频捕获很头疼,主要是由于 ffmpeg/gstreamer 安装错误。

保存视频

所以我们捕获一个视频并逐帧处理它,我们想要保存该视频。对于图像,它非常简单:只需使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 (.mkv)。

FourCC 代码作为 MJPG 的 `cv.VideoWriter_fourcc('M','J','P','G') or cv.VideoWriter_fourcc(*'MJPG')` 传递。

下面的代码从相机捕获,在垂直方向翻转每一帧,并保存视频。

import numpy as np
import cv2 as cv
cap = cv.VideoCapture(0)
# Define the codec and create VideoWriter object
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("Can't receive frame (stream end?). Exiting ...")
        break
    frame = cv.flip(frame, 0)
    # write the flipped frame
    out.write(frame)
    cv.imshow('frame', frame)
    if cv.waitKey(1) == ord('q'):
        break
# Release everything if job is finished
cap.release()
out.release()

其他资源

练习

OpenCV-Python入门 下

目录

鼠标操作与响应

  • 鼠标Callback函数

  • 应用:使用鼠标进行矩形绘制

b1 = cv.imread(r"..\\dataset\\Image\\The_Witcher_Lover.jpg")
img = np.copy(b1)
x1 = -1
x2 = -1
y1 = -1
y2 = -1

def mouse_drawing(event, x, y, falgs, param):
    global x1, y1, x2, y2
    if event == cv.EVENT_LBUTTONDOWN:
        x1 = x
        y1 = y
    if event == cv.EVENT_MOUSEMOVE:
        if x1 < 0 or y1 < 0:
            return
        x2 = x
        y2 = y
        dx = x2 - x1
        dy = y2 - y1
        if dx > 0 and dy > 0:
            b1[:,:,:] = img[:,:,:]
            cv.rectangle(b1, (x1, y1), (x2, y2), (0, 0, 255), 2, 8, 0)
    if event == cv.EVENT_LBUTTONUP:
        x2 = x
        y2 = y
        dx = x2 - x1
        dy = y2 - y1
        if dx > 0 and dy > 0:
            b1[:,:,:] = img[:,:,:]
            cv.rectangle(b1, (x1, y1), (x2, y2), (0, 0, 255), 2, 8, 0)
        x1 = -1
        x2 = -1
        y1 = -1
        y2 = -1

def mouse_demo():
    cv.namedWindow("mouse_demo", cv.WINDOW_AUTOSIZE)
    cv.setMouseCallback("mouse_demo", mouse_drawing)
    while True:
        cv.imshow("mouse_demo", b1)
        c = cv.waitKey(10)
        if c == 27:
            break
    cv.destroyAllWindows()

图像像素类型转换与归一化

  • 归一化方法支持
  • 归一化函数

cv.normalize(src, dst[, alpha[, beta[, norm_type[, dtype[, mask]]]]] ) -> dst
src表示输入图像, dst表示输出图像,alpha, beta 默认是1, 0,是归一化的区间值
norm_type默认是NORM_L2,norm_type常用是NORM_MINMAX

  • 注意:
def norm_demo():
    image_uint8 = cv.imread(r"..\\dataset\\Image\\The_Witcher_Lover.jpg")
    cv.imshow("input", image_uint8)
    img_f32 = np.float32(image_uint8)
    cv.imshow("imgf32", img_f32)
    cv.normalize(img_f32, img_f32, 1, 0, cv.NORM_MINMAX)
    cv.imshow("norm_imgf32", img_f32)
    cv.waitKey(0)
    cv.destroyAllWindows()

图像几何变换

  • 平移变换
  • 放缩变换
  • 旋转变换
  • 旋转矩阵获取函数

cv.getRotationMatrix2D
Center表示旋转中心, angle表示度数,大于零表示逆时针旋转, scale表示放缩尺度大小

  • 几个旋转操作函数

cv.flip(src, flipCode[, dst] ) ->dst
cv.rotate(src, rotateCode[, dst] ) -> dst
src表示输入图像
flipCode支持0水平、1垂直,-1对角线翻转,rotateCode支持旋转90°,180°,270°

def affine_demo():
    image = cv.imread(r"..\\dataset\\Image\\The_Witcher_Lover.jpg")
    h, w, c = image.shape
    cx = int(w / 2)
    cy = int(h / 2)
    cv.imshow("image", image)

    M = np.zeros((2, 3), dtype=np.float32)
    M[0, 0] = 3
    M[1, 1] = 3
    M[0, 2] = 0
    M[1, 2] = 0
    dst = cv.warpAffine(image, M, (int(w*3), int(h*3)))
    cv.imshow("rescale-demo", dst)

    M = cv.getRotationMatrix2D((w/2, h/2), 45.0, 1.0)
    dst = cv.warpAffine(image, M, (w, h))
    cv.imshow("rotate-demo", dst)

    dst = cv.flip(image, 0)
    cv.imshow("flip-demo", dst)

    cv.waitKey(0)
    cv.destroyAllWindows()

视频读写处理

  • 视频标准与格式

SD(Standard Definition)标清480P
HD(High Definition)高清720P/1080P
UHD(Ultra High Definition)超高清4K/2160P
分辨率表示
SD-640x480, 704x480, 720x480, 848x480等
HD-960x720,1280x720,1440x1080,1920x1080
UHD-4K,2160P

  • 视频读取函数
  • 视频文件保存

cv.VideoWriter( filename, fource, fps, framesize[, isColor]-> <videoWriter object>
filename保存文件名称,fourcc编码方式,fps帧率,frameSize 视频帧大小,与实现大小相符

  • 查询视频属性
def video_demo():
    cap = cv.VideoCapture("vtest.avi")
    # query video file metadata
    fps = cap.get(cv.CAP_PROP_FPS)
    frame_w = cap.get(cv.CAP_PROP_FRAME_WIDTH)
    frame_h = cap.get(cv.CAP_PROP_FRAME_HEIGHT)
    print(fps, frame_w, frame_h)
    # encode mode
    fourcc = cap.get(cv.CAP_PROP_FOURCC)
    # create video writer
    writer = cv.VideoWriter("output.mp4", int(fourcc), fps, (int(frame_w), int(frame_h)))
    # loop read frame until last frame
    while True:
        ret, frame = cap.read()
        if ret is not True:
            break
        cv.imshow("frame", frame)
        c = cv.witKey(1)
        if c == 27:
            break
        writer.write(frame)

图像直方图

  • 图像直方图函数

hist = cv.calcHist([image], [i], None, [32], [0, 256])
image输入图像,i表示通道索引,mask=None,histSize表示bin的个数,表示通道的取值范围0~256

def image_hist():
    image = cv.imread(r"..\\dataset\\Image\\The_Witcher_Lover.jpg")
    cv.imshow("input", image)
    color = ('blue', 'green', 'red')
    for i, color in enumerate(color):
        hist = cv.calcHist([image], [i], None, [32], [0, 256])
        print(hist)
        plt.plot(hist, color=color)
        plt.xlim([0, 32])
    plt.show()
    cv.waitKey(0)
    cv.destroyAllWindows()

图像直方图均衡化

  • 直方图均衡化原理

  • 直方图均衡化函数

cv.equalizeHist(src[, dst]) -> dst
src必须是八位单通道图像,dst返回结果图像,类型与src保持一致

def eq_demo():
    image = cv.imread(r"..\\dataset\\Image\\The_Witcher_Lover.jpg", cv.IMREAD_GRAYSCALE)
    cv.imshow("input", image)
    hist = cv.calcHist([image], [0], None, [32], [0, 256])
    print(hist.dtype)
    plt.plot(hist, color="gray")
    plt.xlim([0, 32])
    plt.show()
    cv.waitKey(0)

    eqimg = cv.equalizeHist(image)
    hist = cv.calcHist([eqimg], [0], None, [32], [0, 256])
    print(hist.dtype)
    plt.plot(hist, color="gray")
    plt.xlim([0, 32])
    plt.show()
    cv.waitKey(0)
    cv.destroyAllWindows()

图像卷积操作

  • 图像卷积的定义
  • 卷积的边缘填充

边缘处理,边缘填充的方式
cv.BORDER_DEFAULT gfedcb|abcdefgh|gfedcba
cv.BORDER_WRAP cdefgh|abcdefgh|abcdefg
cv.BORDER_CONSTANT iiiiii|abcdefgh|iiiiiii

  • 卷积模糊函数

v.blur( src, ksize[, dst[, anchor[, borderType]]]) -> dst
src表示输入图像 CV_8U, CV_32F or CV_64F
Ksize卷积核大小
Anchor锚定位置
borderType边缘处理方式

def conv_demo():
    image = cv.imread("lena.png")
    dst = np.copy(image)
    cv.imshow("input", image)
    h, w, c = image.shape
    for row in range(1, h-1, 1):
        for col in range(1, w-1, 1):
            m = cv.mean(image[row-2 : row+2, col-2:col+2])
            dst[row, col] = (int(m[0]), int(m[1]), int(m[2]))
    cv.imshow("conv-demo", dst)

    blured = cv.blur(image, (5, 5), anchor=(-1, -1))
    cv.imshow("blur-demo", blured)

    cv.waitKey(0)
    cv.destroyAllWindows()

高斯模糊

  • 高斯模糊函数

cv.GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]]) ->dst
ksize必须是正数而且是奇数
sigmaX高斯核函数X方向标准方差,sigmaY高斯核函数Y方向标准方差,默认0,表示跟sigmaX相同,
ksize为0表示从sigmaX计算生成ksize,ksize大于0表示从ksize计算生成sigmaX

  • 均值模糊与高斯模糊
    均值模糊 – 卷积核权重系数相同
    高斯模糊 – 卷积核根据高斯函数生成,权重系数不同
def conv_demo():
    image = cv.imread("lena.png")
    cv.imshow("input", image)

    g1 = cv.GaussianBlur(image, (0, 0), 15)
    g2 = cv.GaussianBlur(image, (15, 15), 15)
    cv.imshow("GaussianBlur-demo1", g1)
    cv.imshow("GaussianBlur-demo2", g2)

    cv.waitKey(0)
    cv.destroyAllWindows()

像素重映射

  • 像素重映射
    把像素点P(x,y)重新映射到一个新的位置P’(x’, y’)
  • 像素重映射函数

cv.remap(src, map1, map2, interpolation[, dst[, borderMode[, borderValue]]] ) -> dst
src表示图像
map1表示x,y方向映射规则,或者x方向映射
map2如果map1表示x,y映射时为空,否则表示y
表示映射时候的像素插值方法,支持:INTER_NEAREST 、NTER_LINEAR 、NTER_CUBIC

def remap_demo():
    image = cv.imread("lena.png")
    cv.namedWindow("remap-demo", cv.WINDOW_AUTOSIZE)
    cv.createTrackbar("remap-type", "remap-demo", 0, 3, trackbar_callback)
    h, w, c = image.shape
    cv.imshow("input", image)
    map_x = np.zeros((h, w), dtype=np.float32)
    map_y = np.zeros((h, w), dtype=np.float32)
    while True:
        pos = cv.getTrackbarPos("remap-type", "remap-demo")
        #倒立
        if pos == 0:
            for i in range(map_x.shape[0]):
                map_x[i, :] = [x for x in range(map_x.shape[1])]
            for j in range(map_x.shape[1]):
                map_y[:, j] = [map_y.shape[0] - y for y in range(map_y.shape[0])]
        #镜像
        elif pos == 1:
            for i in range(map_x.shape[0]):
                map_x[i, :] = [map_x.shape[1] - x for x in range(map_x.shape[1])]
            for j in range(map_x.shape[1]):
                map_y[:, j] = [y for y in range(map_y.shape[0])]
        #对角线对称
        elif pos == 2:
            for i in range(map_x.shape[0]):
                map_x[i, :] = [map_x.shape[1] - x for x in range(map_x.shape[1])]
            for j in range(map_x.shape[1]):
                map_y[:, j] = [map_y.shape[0] - y for y in range(map_y.shape[0])]
        #放大两倍
        elif pos == 3:
            for i in range(map_x.shape[0]):
                map_x[i, :] = [int(x/2) for x in range(map_x.shape[1])]
            for j in range(map_x.shape[1]):
                map_y[:, j] = [int(y/2) for y in range(map_y.shape[0])]

        dst = cv.remap(image, map_x, map_y, cv.INTER_LINEAR)
        cv.imshow("remap-demo", dst)
        c = cv.waitKey(100)
        if c == 27:
            break
    cv.waitKey(0)
    cv.destroyAllWindows()

图像二值化

  • 图像二值化的定义
    图像二值化就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果的过程。图像的二值化使图像中数据量大为减少,从而能凸显出目标的轮廓,是一种简单的图像分割方法。
    进行二值化有多种方式,其中最常用的就是采用阈值法(Thresholding)进行二值化。阈值法又分为全局阈值(Global Method)和局部阈值(Local Method),又称自适应阈值(Adaptive Thresholding)。
  • 二值化函数

cv.threshold( src, thresh, maxval, type[, dst]) -> retval, dst
src表示输入图像,thresh表示阈值,maxval表示最大值
type表示二值化THRESH_BINARY或者二值化反THRESH_BINARY_INV
retval表示返回阈值,dst返回的二值图像

def binary_demo():
    #灰度图像
    image = cv.imread(r"..\\dataset\\Image\\The_Witcher_Lover.jpg")
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    cv.imshow("gray", gray)

    #手动阈值,二值化
    ret1, binary1 = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
    cv.imshow("binary1", binary1)

    #求均值,二值化
    m = cv.mean(gray)[0]
    ret2, binary2 = cv.threshold(gray, m, 255, cv.THRESH_BINARY)
    cv.imshow("binary2", binary2)

    cv.waitKey(0)
    cv.destroyAllWindows()

全局与自适应二值化

  • 全局二值化

OTSU法/大津法

三角法
图像处理之三角法图像二值化

cv.threshold( src, thresh, maxval, type[, dst])->retval, dst
type表示二值化
THRESH_BINARY | THRESH_OTSU
THRESH_BINARY | THRESH_TRIANGLE
THRESH_BINARY_INV | THRESH_OTSU
表示不同的全局二值化方法

  • 自适应二值化

cv.adaptiveThreshold(src, ma

以上是关于OpenCV-python视频入门的主要内容,如果未能解决你的问题,请参考以下文章

[OpenCV-Python] OpenCV 中的 Gui 视频

OpenCV-Python 视频读取

使用opencv-python录视频

[OpenCV-Python] OpenCV 中视频分析 部分 VI

opencv-python 极速入门

初识OpenCV-Python - 005: 识别视频中的蓝色