On this page

    OpenCV

    安装

    python安装

    pip install opencv-contrib-python
    

    API

    cv2.imread

    读取图片

    cv2.imread(path, flags)

    如果给定的图片路径不对,该函数不会抛出异常,而是返回一个None

    cv2.imshow

    显示图片

    cv2.imshow(winname, mat)

    在图片显示的过程中,通常会伴随几个其他的函数,他们分别是:

    如果没有cv2.waitKey()函数,图像不会显示(也许是一闪而过,我们人眼观察不到)

    cv2.destroyAllWindows()用来销毁所有已经创建的窗口, 如果需要销毁指定窗口使用cv2.destroyWindow()函数,他接受一个表示窗口名字的名字。

    直接用cv2.imshow()创建的窗口是自动适应图片大小的,不能缩放,如果我们想放大缩小窗口,必须单独用cv2.namedWindow(),并通过flag参数指定窗口模式为cv2.WINDOW_NORMAL,默认为cv2.WINDOW_AUTOSIZE.

    cv2.imwrite

    保存图片

    cv2.imwrite(filename, img)

    cv2.cvtColor

    cvtColor(src,dst,code,dstCn) ===> (原图像,输出图像,color转化代码,输出通道)

    画矩形和文字注释

    cv2.rectangle(img, pt1, pt2, color[, thickness[, lineType[, shift]]])

    ##!/usr/bin/python
    ## -*- coding: UTF-8 -*-
    """
    @Time    : 2018-11-13 21:03
    @Author  : jianjun.wang
    @Email   : alanwang6584@gmail.com
    """
    
    import numpy as np
    import cv2 as cv
     
    img = np.zeros((320, 320, 3), np.uint8) ##生成一个空灰度图像
    print img.shape ## 输出:(320, 320, 3)
    
    ## 矩形左上角和右上角的坐标,绘制一个绿色矩形
    ptLeftTop = (60, 60)
    ptRightBottom = (260, 260)
    point_color = (0, 255, 0) ## BGR
    thickness = 1 
    lineType = 4
    cv.rectangle(img, ptLeftTop, ptRightBottom, point_color, thickness, lineType)
    
    ## 绘制一个红色矩形
    ptLeftTop = (120, 100)
    ptRightBottom = (200, 150)
    point_color = (0, 0, 255) ## BGR
    thickness = 1
    lineType = 8
    cv.rectangle(img, ptLeftTop, ptRightBottom, point_color, thickness, lineType)
    
    cv.namedWindow("AlanWang")
    cv.imshow('AlanWang', img)
    cv.waitKey (10000) ## 显示 10000 ms 即 10s 后消失
    cv.destroyAllWindows()
    
    

    cv2.putText(img, ‘lena’, (50,150), cv2.FONT_HERSHEY_COMPLEX, 5, (0, 255, 0), 12)

    cv2.resize

    img_test2 = cv.resize(img, (0, 0), fx=0.25, fy=0.25, interpolation=cv.INTER_NEAREST)
    

    cv2.circle

    cv2.circle(img, center, radius, color[, thickness[, lineType[, shift]]])

    circle() 函数可以绘制以一个点为圆心特定半径的圆

    cv2.line

    cv2.line(plot,(0,y),(int(h * mul),y),(255,0,0),w)
    

    cv2.remap

    cv.remap( src, map1, map2, interpolation[, dst[, borderMode[, borderValue]]] )

    重映射,就是把一幅图像中某位置的像素放置到另一个图片指定位置的过程。

    在国内的网站上讲这个函数都十分简单, 并不清楚, 在stackoverflow找到了好的解释。

    首先,mapx和mapy在干什么?

    mapx和mapy每个对应位置取出来一个点, 可以确定地对应到原图中的一个点,将这个点的像素值赋值给mapx和mapy对应矩阵的位置, 如果mapx和mapy是非整数, 那么会采用插值的方式来确定这个点的像素值, 举个例子:

    如果是一一对应的关系:

    	img = np.uint8(np.random.rand(8, 8)*255)
        print(img)
        ##array([[230,  45, 153, 233, 172, 153,  46,  29],
        ##       [172, 209, 186,  30, 197,  30, 251, 200],
        ##       [175, 253, 207,  71, 252,  60, 155, 124],
        ##       [114, 154, 121, 153, 159, 224, 146,  61],
        ##       [  6, 251, 253, 123, 200, 230,  36,  85],
        ##       [ 10, 215,  38,   5, 119,  87,   8, 249],
        ##       [  2,   2, 242, 119, 114,  98, 182, 219],
        ##       [168,  91, 224,  73, 159,  55, 254, 214]], dtype=uint8)
    
        map_y = np.array([[0, 1], [2, 3]], dtype=np.float32)
        map_x = np.array([[5, 6], [7, 10]], dtype=np.float32)
        mapped_img = cv2.remap(img, map_x, map_y, cv2.INTER_LINEAR)
        ##array([[153, 251],
        ##     [124,   0]], dtype=uint8)
    
    map_y
    =====
    0  1
    2  3
    
    map_x
    =====
    5  6
    7  10
    

    可以看到有mapx = 5, mapy = 0确定的点为第0行,第5列的像素值, 该值为153。其他的点类似。

    那么当mapx和mapy为非整数的时候:

    	img = np.uint8(np.random.rand(8, 8)*255)
        ##array([[230,  45, 153, 233, 172, 153,  46,  29],
        ##       [172, 209, 186,  30, 197,  30, 251, 200],
        ##       [175, 253, 207,  71, 252,  60, 155, 124],
        ##       [114, 154, 121, 153, 159, 224, 146,  61],
        ##       [  6, 251, 253, 123, 200, 230,  36,  85],
        ##       [ 10, 215,  38,   5, 119,  87,   8, 249],
        ##       [  2,   2, 242, 119, 114,  98, 182, 219],
        ##       [168,  91, 224,  73, 159,  55, 254, 214]], dtype=uint8)
    
        map_y = np.array([[0, 1.5], [2, 3]], dtype=np.float32)
        map_x = np.array([[5, 6], [7, 10]], dtype=np.float32)
        mapped_img = cv2.remap(img, map_x, map_y, cv2.INTER_LINEAR)
        ##array([[153, 203],
        ##      [124,   0]], dtype=uint8)
    

    可以看到,当我把mapx中的1改为1.5后,对应的值变成了203, 这个值怎么算出来的。

    首先mapy对应第6列, mapx在第1列和第2列之间, 通过以下公式计算得到:

    \[dst = 0.5 * 251 + 0.5 * 155\]

    并不是所有的插值都是按照这样的比例关系算的,但是意思是这么个意思。

    上述的行和列都从0开始算起。

    cv2.warpAffine

    cv2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]])

    cv2.getRotationMatrix2D

    getRotationMatrix2D(center, angle, scale)

    cv2.copyMakeBorder

    给图片做填充(paddinng)

    cv2.copyMakeBorder(img,120,120,0,0,cv2.BORDER_CONSTANT,value=[0, 0, 0])

    cv2.addWeighted

    cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]]) → dst

    等价于下面的公式:

    \[dst = src1 * alpha + src2 * beta + gamma;\]

    cv2.canny

      Canny(
        inputArray src, //8-bit的输入图像
        OutputArray edges, //输出边缘图像, 一般都是二值图像
        double threshold1, //低阈值, 常取高阈值的1/2, 或者1/3
        double threshold2, //高阈值
        int aptertureSize, //Sobel算子的size, 通常3x3, 取值3
        boot L2gradient //选择true表示是L2来归一化, 否则用L1归一化
        )
    

    cv2.VideoWriter_fourcc

    VideoWriter_fourcc为视频编解码器, fourcc意为四字符代码(Four-Character Codes),顾名思义,该编码由四个字符组成,下面是VideoWriter_fourcc对象一些常用的参数,注意:字符顺序不能弄混

    fourcc为 四个字符用来表示压缩帧的codec 例如:

    cv2.videowriter

    VideoWriter(filename, fourcc, fps, frameSize[, isColor]) -> <VideoWriter object>
    

    读取视频帧率、分辨率、读取视频总帧数

    #——————————————————————————————
    #————————添加自己的视频播放路径———————————
    video_path="F:/Zeng-20180622/Video180621/1.flv"
     
    # 创建一个视频读写类
    video_capture=cv2.VideoCapture(video_path)
     
    #读取视频的fps,  大小
    fps=video_capture.get(cv2.CAP_PROP_FPS)
    size=(video_capture.get(cv2.CAP_PROP_FRAME_WIDTH),video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
    print("fps: {}\nsize: {}".format(fps,size))
     
    #读取视频时长(帧总数)
    total = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
    print("[INFO] {} total frames in video".format(total))
     
    #设定从视频的第几帧开始读取
    #From :  https://blog.csdn.net/luqinwei/article/details/87973472
    frameToStart = 2000
    video_capture.set(cv2.CAP_PROP_POS_FRAMES, frameToStart);
    

    使用键盘控制视频

    #--------键盘控制视频---------------
        #读取键盘值
        key = cv2.waitKey(1) & 0xff
        #设置空格按下时暂停
        if key == ord(" "):
            cv2.waitKey(0)
        #设置Q按下时退出
        if key == ord("q"):
            break
    

    显示视频播放进度

        #显示当前视频已播放时间和总时间
        #计算当前
        now_seconds=int(current_frame /fps%60)
        now_minutes=int(current_frame/fps/60)
        total_second=int(total /fps%60)
        total_minutes=int(total/fps/60)
        #   { <参数序号> : <填充> <对齐)> <宽度> <,> <.精度> <类型>}.
        Time_now_vs_total="Time:{:>3}:{:>02}|{:>3}:{:0>2}".format(now_minutes,now_seconds,total_minutes,total_second)
        print(Time_now_vs_total)
    

    CV概念知识

    图像格式

    HSV图像: 色调(H), 饱和度(S), 亮度(V)

    LAB图像: 亮度(L), 红色至绿色的范围(A),蓝色至黄色的范围(B)

    形态学操作

    腐蚀:白像素的连同域变小

    膨胀: 白像素的连同域变大

    开操作:先膨胀再腐蚀, 会让相近的小点连同起来

    闭操作:先腐蚀再膨胀, 会让一些小点消失

    Trick

    视频的加载和保存

    编程实现从视频文件中读取并显示视频

    import cv2
     
    cap = cv2.VideoCapture(vtest.avi)##打开相机
     
    while( True):
        ret,frame = cap.read()##捕获一帧图像
        if ret:
            cv2.imshow('frame',frame)
            cv2.waitKey(25)
        else:
            break
     
    cap.release()##关闭相机
    cv2.destroyAllWindows()##关闭窗口
    

    仿射变换(Affine Transformation)

    图像的几何变换——拉伸、收缩、扭曲、旋转(stretch,shrink,distortion,rotation)

    拉伸、收缩、扭曲、旋转是图像的几何变换,在三维视觉技术中大量应用到这些变换,又分为仿射变换和透视变换。仿射变换通常用单应性(homography)建模,利用cvWarpAffine解决稠密仿射变换,用cvTransform解决稀疏仿射变换。仿射变换可以将矩形转换成平行四边形,它可以将矩形的边压扁但必须保持边是平行的,也可以将矩形旋转或者按比例变化。透视变换提供了更大的灵活性,一个透视变换可以将矩阵转变成梯形。当然,平行四边形也是梯形,所以仿射变换是透视变换的子集。

    算法原理简介

    首先,我们需要使用轮廓检测等其它方法获取到目标的4个关键点坐标值;然后利用相应的变换关系获取到新的4个坐标点;接着利用这4对关键点计算出仿射变换矩阵M;最后应用仿射变换矩阵到目标中即可。

    算法实现步骤

    cv2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]])

    cv.GetAffineTransform(src, dst, mapMatrix) → None

    M=cv2.getAffineTransform(pos1,pos2),其中两个位置就是变换前后的对应位置关系。输出的就是仿射矩阵M。然后在使用函数cv2.warpAffine()。

    更换绿色背景

    HSV过滤图片的操作

    H(色调):0-180

    S(饱和度):0-255

    V(黑暗的程度):0-255

    下图是HSV的颜色的取值范围,根据范围取值可以过滤掉某种颜色:

    def change_bg(self, frame, index=0):
            height, width, channel = frame.shape
            bg = np.zeros((height, width, channel))
            if index == 0:
                bg[:, :, 0] = 255
                bg[:, :, 1] = 0
                bg[:, :, 2] = 0
            elif index == 1:
                bg[:, :, 0] = 0
                bg[:, :, 1] = 255
                bg[:, :, 2] = 0
            elif index == 2:
                bg[:, :, 0] = 0
                bg[:, :, 1] = 0
                bg[:, :, 2] = 255
            elif index == 3:
                bg[:, :, 0] = 255
                bg[:, :, 1] = 255
                bg[:, :, 2] = 0
            elif index == 4:
                bg[:, :, 0] = 0
                bg[:, :, 1] = 255
                bg[:, :, 2] = 255
            else:
                bg[:, :, 0] = 255
                bg[:, :, 1] = 0
                bg[:, :, 2] = 255
    
            hsv=cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)#进行色彩值转换,RGB到HSV
            lower_hsv = np.array([35,43,46])#色彩范围h s v三变量的最小取值
            upper_hsv = np.array([77, 255, 255])#色彩范围h s v三变量的最小取值
            mask=cv2.inRange(hsv,lowerb=lower_hsv,upperb=upper_hsv)  #进行色值去范围,取出对应的色彩范围进行过滤
    
            mask_bg = mask / 255.
            mask_fg=cv2.bitwise_not(mask) / 255.
    
            frame[:, :, 0] = frame[:, :, 0] * mask_fg + bg[:, :, 0] * mask_bg 
            frame[:, :, 1] = frame[:, :, 1] * mask_fg + bg[:, :, 1] * mask_bg 
            frame[:, :, 2] = frame[:, :, 2] * mask_fg + bg[:, :, 2] * mask_bg 
    
    
            return frame
    

    PIL(Image)用法

    open

    >>> from PIL import Image
    >>> im = Image.open("hopper.ppm")
    
    >>> print(im.format, im.size, im.mode)
    PPM (512, 512) RGB
    

    读取出来就是RGB三通道的。

    有些代码使用 convert(‘RGB) , 以前读取出来似乎默认是RGBA。

    显示Image类型图像

    方法1:

    img.show()
    

    方法2:

    plt.imshow(img)
    plt.show()
    

    transpose方法

    im.transpose(method)⇒ image

    返回当前图像的翻转或者旋转的拷贝。变量方法的取值为:FLIP_LEFT_RIGHT,FLIP_TOP_BOTTOM,ROTATE_90,ROTATE_180,或ROTATE_270。

    Image、numpy转化

    Image转numpy:

    img = numpy.array(im)

    numpy转Image:

    img = Image.fromarray(img.astype(‘uint8’)).convert(‘RGB’)

    FFMPEG

    读取YUV格式视频

    ffplay -f rawvideo -video_size 1920x1080 a.yuv
    

    完整命令

    ffmpeg -f rawvideo -vcodec rawvideo -s 1920x1080 -r 25 -pix_fmt yuv420p -i inputfile.yuv -c:v libx264 -preset ultrafast -qp 0 output.mp4
    

    对所有其他参数的一点解释:

    修改FPS

    1. 降低FPS,增加视频时长

    说对于输入文件是40帧的媒体,我们可以通过

    ffmpeg -r 80 -i input output
    ffmpeg -r 20-i input output
    

    使其输出文件的时长分别为原来的一半和两倍,即播放速度为原来的两倍和一半。

    2. 降低视频FPS,视频时长不变,视频质量变差

    如果我们认为40帧的文件码流太大,我们可以通过降低输出文件帧率的方法降低码流:

    ffmpeg -i input -r 20 output
    

    这样输出文件的码流理论上为输入文件的一半左右。

    -r 所在位置不同,决定其参数是对输入视频还是输出视频起作用。

    Bug总结

    ‘depth’ is 6 (CV_64F)

    CV_64F表示numpy数组’dtype’是64位浮点opencv只适用于’float32’(32位浮点),其中imshow的图像范围是0.0-1.0或’uint8’(无符号8位)0-255。用8位显示更高位图像自然会报错。 同时附上数据类型mat和位数关系: http://blog.sina.com.cn/s/blog_662c7859010105za.html

    Reference

    1. OpenCV——图片的加载、显示、保存(python)
    2. 【Python+OpenCV入门学习】四、视频的读取、显示、保存
    3. Python+Opencv4点仿射变换
    4. Opencv-Python学习笔记五——图像翻转,平移,仿射及透视 warpAffine
    5. [python]OpenCV之cvtColor
    6. Python-OpenCV 图像叠加or图像混合加权(cv2.addWeighted)
    7. How do I use OpenCV’s remap function?
    8. opencv-python图片边框填充(padding)
    9. opencv关于图像imshow显示报错’depth’ is 6 (CV_64F)
    10. Python 用 OpenCV 画矩形 (4)
    11. python下的opencv画矩形和文字注释
    12. Python 用 OpenCV 画点和圆 (2)
    13. ubuntu中ffmpeg修改视频FPS,帧数
    14. 【Python-opencv3.4】视频基本操作(帧率,总视频帧数、从第N帧开始播放、播放进度显示、按键控制视频)
    15. 【OpenCV3.3+Python3.6】图片的HSV色彩空间过滤颜色