目录

标签: OpenCV

OpenCV-Python 获取近似轮廓

根据上一篇文章,我们简单的获取了轮廓的所有坐标点,实际上也许我们只需要局部的几个坐标点即可,如下图所示:

原图
以数值0.1精度取得 4 个点
以 0.01 精度取得多个点
直接注释代码,获得所有点

代码如下所示:

import numpy as np
import cv2 as cv

# 读取图片
im = cv.imread('test_img.png')
imgray = cv.cvtColor(im,cv.COLOR_BGR2GRAY)

# 获取轮廓
ret, thresh = cv.threshold(imgray,127,255,0)
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE,cv.CHAIN_APPROX_SIMPLE)

# 注释以下代码,或 调整数值 “0.1” 以获得不同的精度
epsilon = 0.01*cv.arcLength(contours[0],True)
contours = cv.approxPolyDP(contours[0],epsilon,True)


# 展示
cv.drawContours(im,contours, -1,(0,255,0),3)
cv.imshow('result',im)
cv.waitKey(0)

# 释放资源
cv.destroyAllWindows()

示例代码打包下载:

参考原文:https://docs.opencv.org/4.5.5/dd/d49/tutorial_py_contour_features.html

OpenCV-Python 获取图像轮廓坐标点

原图
运行结果

原理:将图片转为灰度图,取得其轮廓。轮廓可以简单地解释为连接所有连续点(沿边界)的曲线,具有相同的颜色或强度。轮廓是形状分析和对象检测和识别的有用工具。

代码如下:

import numpy as np
import cv2 as cv

# 读取图片
im = cv.imread('test.png')
imgray = cv.cvtColor(im,cv.COLOR_BGR2GRAY)

# 获取轮廓
ret, thresh = cv.threshold(imgray,127,255,0)
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE,cv.CHAIN_APPROX_SIMPLE)

# 展示
cv.drawContours(im,contours, -1,(0,255,0),3)
cv.imshow('result',im)
cv.waitKey(0)

# 释放资源
cv.destroyAllWindows()

代码中的 test.jpg 可以直接在本文中找到,另存为和代码一同运行即可得到如上效果

参考原文:https://docs.opencv.org/4.5.5/d4/d73/tutorial_py_contours_begin.html

OpenCV-Python 获取特定颜色范围内的图像区域

本示例运行需要计算机配置有摄像头,如果您没有摄像头也没有关系,可以改变cap = cv.VideoCapture(0)代码,读取一段视频文件来演示也是可以的。

将 BGR 图像转换为 HSV,我们可以使用它来提取彩色对象。在 HSV 中,表示颜色比在 BGR 颜色空间中更容易。在我们的应用程序中,我们将尝试提取蓝色对象。所以这里是方法:

  • 拍摄视频的每一帧
  • 从 BGR 转换为 HSV 颜色空间
  • 我们将 HSV 图像阈值设置为蓝色范围
  • 现在单独提取蓝色对象,我们可以在该图像上做任何我们想做的事情。

示例代码如下:

import cv2 as cv
import numpy as np

cap = cv.VideoCapture(0)

while(1):
    # 获取每一帧数据
    _, frame = cap.read()

    # 将图像从 BGR 转 HSV
    hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)

    # 定义 HSV 中的黄色范围
    lower_blue = np.array([110,50,50])
    upper_blue = np.array([130,255,255])

    # 对 HSV 图像设置阈值,使其只显示蓝色
    mask = cv.inRange(hsv, lower_blue, upper_blue)

    # 使用 mask 遮罩层显示图像
    res = cv.bitwise_and(frame,frame, mask=mask)

    cv.imshow('frame', frame)
    cv.imshow('mask', mask)
    cv.imshow('res',res)
    k = cv.waitKey(5) % 0xFF
    if k == 27:
        break

cv.destroyAllWindows()

如何获取一个HSV值

我们可以通过 python 终端直接将一个 BGR 值,转换为 HSV 值,示例如下:

>>> green = np.uint8([[[0,255,0 ]]])
>>> hsv_green = cv.cvtColor(green,cv.COLOR_BGR2HSV)
>>> print( hsv_green )
[[[ 60 255 255]]]

参考原文:https://docs.opencv.org/4.5.5/df/d9d/tutorial_py_colorspaces.html

OpenCV-Python 获取一段代码运行的所消耗的时间

在图像处理中,由于您每秒处理大量操作,因此您的代码不仅要提供正确的解决方案,而且还要以最快的方式提供解决方案,通过 OpenCV 自带的函数可以进行时间差计算,示例代码如下:

import cv2 as cv

# 获取当前时间(1)
e1 = cv.getTickCount()

# 你的一些耗时的代码
for i in range(10000):
    print(i)

# 获取当前时间(2)
e2 = cv.getTickCount()
# 计算时间差
time = (e2 - e1) / cv.getTickFrequency()

# 打印耗时
print('耗时(秒)',time)

运行效果如下:

....
9996
9997
9998
9999
耗时(秒) 0.6926358

OpenCV-Python 抠图,并将抠出的图重叠到另一张图片中

主要工作原理为将图置为灰度图,根据阈值取出相应的灰度值转换为0 或 1(非黑即白)的图像,然后对黑或白区域进行抠图取得区域,然后进行抠图。效果如下:

实现代码如下:

import cv2 as cv

# 加载两张图片
img1 = cv.imread('messi.jpeg')
img2 = cv.imread('opencv-logo.png')

# 我希望把 LOGO 防止到左上角,所以创建了一个区域(roi)
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols]

# 将 LOGO 图转换为灰度图,阈值为 10, 最大值为 255 
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)
mask_inv = cv.bitwise_not(mask)

# 现在将 ROI 抠出黑色区域
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv)

# 抠出 LOGO 图像
img2_fg = cv.bitwise_and(img2,img2,mask = mask)

# 将抠出的 LOGO 防入 ROI 区域中
dst = cv.add(img1_bg,img2_fg)
img1[0:rows, 0:cols ] = dst

# 显示图像
cv.imshow('res',img1)

# 退出,并释放资源
cv.waitKey(0)
cv.destroyAllWindows()

本文示例完整代码下载;

OpenCV-Python 图像混合(图像加权法)

这也是图像添加,但为图像赋予不同的权重,以产生混合或透明的感觉。

示例代码如下:

import cv2 as cv

# 读取图像文件
img1 = cv.imread('ml.png')
img2 = cv.imread('opencv-logo.png')

# 图像重叠
dst = cv.addWeighted(img1,0.3,img2,0.7,0)

# 显示图像
cv.imshow('dst',dst)
cv.waitKey(0)

# 退出程序,释放资源
cv.destroyAllWindows()

这里设置了第一张图片的权重为 0.3, 第二张图片为 0.7 所混合出来的效果。(注意是权重混合,并不是透明度设置,具体可参考官方文档:https://docs.opencv.org/4.5.5/d2/de8/group__core__array.html#gafafb2513349db3bcff51f54ee5592a19)

示例代码下载:

OpenCV-Python 创建轨迹栏控件

在这里,我们将创建一个简单的应用程序来显示您指定的颜色。您有一个显示颜色的窗口和三个用于指定 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')

# 创建 3 个分别代表 RGB 的轨迹栏控件
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 个轨迹栏控件的当前位置
    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()

以上代码即完整代码,Copy 运行即可得到相同的结果。

OpenCV-Python 使用鼠标在窗口中绘制圆形、矩形等

简单演示

在这里,我们创建了一个简单的应用程序,它可以在我们双击图像的任何位置绘制一个圆圈。

首先我们创建一个鼠标回调函数,当鼠标事件发生时执行。鼠标事件可以是与鼠标相关的任何事件,例如左键按下、左键按下、左键双击等。它为我们提供了每个鼠标事件的坐标 (x,y)。有了这个活动和地点,我们可以做任何我们喜欢的事情。要列出所有可用的事件,请在 Python 终端中运行以下代码:

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

实现以上效果完整代码如下:

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)
    # 按下 Esc 时,退出程序
    if cv.waitKey(20) & 0xFF == 27:
        break

# 当程序结束时,释放资源
cv.destroyAllWindows()

高级演示

现在我们寻求一个更好的应用程序。在此,我们像在 Paint 应用程序中一样通过拖动鼠标来绘制矩形或圆形(取决于我们选择的模式)。所以我们的鼠标回调函数有两部分,一是绘制矩形,二是绘制圆形。这个具体的例子将非常有助于创建和理解一些交互式应用程序,如对象跟踪、图像分割等。

实现以上效果完整代码如下:

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)

# 创建一个黑色背景图像的窗口
img = np.zeros((512,512,3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image',draw_circle)
while(1):
    cv.imshow('image',img)
    # 按下 Esc 退出程序
    k = cv.waitKey(1) & 0xFF

    # 按下 m 键,切换绘图模式
    if k == ord('m'):
        mode = not mode
    elif k == 27:
        break

# 程序退出时,释放资源
cv.destroyAllWindows()

以上代码为完整代码, Copy 运行即可得到相同的结果。

附加说明:以上代码在 Windows 中运行正常,如果您使用的场景为 Mac + 触摸板,则需要更改相应的鼠标捕获事件。

OpenCV-Python 绘制矩形、圆、线条、文字等

OpenCV提供了cv.line()、cv.circle()、cv.rectangle()、cv.ellipse()、cv.putText() 等函数,来让我们可以轻易的在图像中进行绘制一些常见图像信息,本示例将展示这些函数使用效果,本文代码最终运行效果如下:

我在其代码中已描述了各项代码的使用,如需具体了解可参见本文末尾的官方文档。

main.py 代码:

import numpy as np
import cv2 as cv

# 创建一个黑色图像
img = np.zeros((512,512,3), np.uint8)

# 绘制一个 5px 的蓝色线条
# 要绘制一条线,您需要传递线的起点和终点坐标。我们将创建一个黑色图像并在其上从左上角到右下角画一条蓝线。
cv.line(img,(0,0),(511,511),(255,0,0),5)

# 绘制一个矩形
# 要绘制矩形,您需要矩形的左上角和右下角。这次我们将在图像的右上角绘制一个绿色矩形。
cv.rectangle(img,(384,0),(510,128),(0,255,0),3)

# 绘制一个圆
# 要画一个圆,你需要它的中心坐标和半径。我们将在上面绘制的矩形内画一个圆圈。
cv.circle(img, (447,63), 63, (0,0,255), -1)

# 绘制一个椭圆
# 要绘制椭圆,我们需要传递几个参数。一个参数是中心位置 (x,y)。下一个参数是轴长度(长轴长度,短轴长度)。
# angle 是椭圆沿逆时针方向旋转的角度。startAngle 和 endAngle 表示从主轴顺时针方向测量的椭圆弧的起点和终点。
# 即给出值 0 和 360 给出完整的椭圆。有关更多详细信息,请查看cv.ellipse()的文档。下面的示例在图像的中心绘制一个半椭圆。
cv.ellipse(img, (256,256), (100,50), 0,0,180, 255, -1)

# 绘制多边形
# 要绘制多边形,首先需要顶点坐标。将这些点放入一个形状为 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))

# 绘制文本
font = cv.FONT_HERSHEY_SIMPLEX
cv.putText(img, 'OpenCV', (10,500), font, 4, (255,255,255), 2, cv.LINE_AA)

# 显示绘制结果到窗口中
cv.imshow('绘制结果展示',img)
cv.waitKey(0)

以上为本文的完整代码,直接运行即可实现本文所展示的效果。

本示例参考官方原文:https://docs.opencv.org/4.5.5/dc/da5/tutorial_py_drawing_functions.html

OpenCV-Python 保存视频帧

本文代码实现目的:通过当前电脑摄像头,录制一段视频并进行垂直翻转处理后,保存为处理后的视频文件。

实现

所以我们捕获一个当前计算机的摄像头视频流并逐帧处理它,我们想要保存该视频。对于图像,它非常简单:只需使用cv.imwrite(). 在这里,需要做更多的工作。

这次我们创建了一个VideoWriter对象。我们应该指定输出文件名(例如:output.avi)。然后我们应该指定FourCC代码。然后应传递每秒帧数 (fps) 和帧大小。最后一个是isColor标志。如果是True,编码器期望彩色帧,否则它适用于灰度帧。

FourCC是一个 4 字节的代码,用于指定视频编解码器。可用代码列表可在fourcc.org中找到。它依赖于平台。以下编解码器对我来说很好用。

执行效果(最终会输出一个 output.avi 视频文件,也就是当前代码运行结果所录制并处理过的的视频):

main.py 代码:

import numpy as np
import cv2 as cv

# 从当前电脑中的摄像头读取视频流
cap = cv.VideoCapture(0)

width = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))

# 定义编解码器并创建 VideoWriter 对象
fourcc = cv.VideoWriter_fourcc(*'XVID')
out = cv.VideoWriter('output.avi', fourcc, 20.0, (width,  height))

# 处理视频
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()

本示例直接运行以上代码即可(以上即完整代码)

示例参考原文:https://docs.opencv.org/4.5.5/dd/d43/tutorial_py_video_display.html