OpenCV-Python 快速指南



OpenCV Python - 概述

OpenCV 代表 **开源计算机视觉**,它是一个函数库,可用于实时计算机视觉应用程序编程。计算机视觉是指使用计算机程序对数字图像和视频进行分析的一门学科。计算机视觉是现代学科(如人工智能和机器学习)的重要组成部分。

OpenCV 最初由英特尔开发,是一个跨平台库,用 C++编写,但也有 C 接口。许多其他编程语言(如 Java 和 Python)的 OpenCV 包装器也已开发出来。本教程将介绍 OpenCV 的 Python 库的功能。

OpenCV-Python

**OpenCV-Python** 是 OpenCV 库 C++ 实现的 Python 包装器。它利用 NumPy 库进行数值运算,是计算机视觉问题的快速原型工具。

OpenCV-Python 是一个跨平台库,可在所有操作系统 (OS) 平台上使用,包括 Windows、Linux、MacOS 和 Android。OpenCV 还支持图形处理单元 (GPU) 加速。

本教程是为希望在计算机视觉应用领域获得专业知识的计算机科学学生和专业人士设计的。了解 OpenCV-Python 的功能需要具备 Python 和 NumPy 库的预备知识。

OpenCV Python - 环境搭建

在大多数情况下,使用 pip 就足以在您的计算机上安装 OpenCV-Python。

用于安装 pip 的命令如下:

pip install opencv-python

建议在新虚拟环境中执行此安装。当前版本的 OpenCV-Python 为 4.5.1.48,可以通过以下命令验证:

>>> import cv2
>>> cv2.__version__
'4.5.1'

由于 OpenCV-Python 依赖于 NumPy,因此它也会自动安装。或者,您可以安装 Matplotlib 来呈现某些图形输出。

在 Fedora 上,您可以通过以下命令安装 OpenCV-Python:

$ yum install numpy opencv*

也可以从其源代码 http://sourceforge.net 安装 OpenCV-Python。请按照给出的安装说明进行操作。

OpenCV Python - 读取图像

**CV2** 包(OpenCV-Python 库的名称)提供 **imread()** 函数来读取图像。

读取图像的命令如下:

img=cv2.imread(filename, flags)

flags 参数是以下常量的枚举:

  • cv2.IMREAD_COLOR (1) - 加载彩色图像。
  • cv2.IMREAD_GRAYSCALE (0) - 以灰度模式加载图像
  • cv2.IMREAD_UNCHANGED (-1) - 按原样加载图像,包括 alpha 通道

该函数将返回一个图像对象,可以使用 imshow() 函数渲染。使用 imshow() 函数的命令如下:

cv2.imshow(window-name, image)

图像显示在命名窗口中。使用 AUTOSIZE 标志创建新窗口。**WaitKey()** 是一个键盘绑定函数。其参数是以毫秒为单位的时间。

该函数等待指定的毫秒数,并在按下按键之前保持窗口显示。最后,我们可以销毁所有创建的窗口。

该函数等待指定的毫秒数,并在按下按键之前保持窗口显示。最后,我们可以销毁所有创建的窗口。

显示 OpenCV 徽标的程序如下:

import numpy as np
import cv2
# Load a color image in grayscale
img = cv2.imread('OpenCV_Logo.png',1)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

上面的程序显示 OpenCV 徽标如下:

OpenCV Logo

OpenCV Python - 写入图像

CV2 包具有 **imwrite()** 函数,该函数将图像对象保存到指定文件。

使用 imwrite() 函数保存图像的命令如下:

cv2.imwrite(filename, img)

OpenCV 根据文件扩展名自动确定图像格式。OpenCV 支持 *.bmp、*.dib、*.jpeg、*.jpg、*.png、*.webp、*.sr、*.tiff、*.tif 等图像文件类型。

示例

下面的程序加载 OpenCV 徽标图像,并在按下“s”键时保存其灰度版本:

import numpy as np
import cv2
# Load an color image in grayscale
img = cv2.imread('OpenCV_Logo.png',0)
cv2.imshow('image',img)
key=cv2.waitKey(0)
if key==ord('s'):
   cv2.imwrite("opencv_logo_GS.png", img)
cv2.destroyAllWindows()

输出

OpenCV logo greyscale

OpenCV Python - 使用 Matplotlib

Python 的 Matplotlib 是一个功能强大的绘图库,包含大量用于各种绘图类型的绘图函数。它还具有 imshow() 函数来渲染图像。它提供了额外的功能,例如缩放、保存等。

示例

在运行以下程序之前,请确保在当前工作环境中安装了 Matplotlib。

import numpy as np
import cv2
import matplotlib.pyplot as plt
# Load an color image in grayscale
img = cv2.imread('OpenCV_Logo.png',0)
plt.imshow(img)
plt.show()

输出

OpenCV Logo Matplotlib

OpenCV Python - 图像属性

OpenCV 将图像数据读取到 NumPy 数组中。此 ndarray 对象的 **shape()** 方法揭示了图像属性,例如尺寸和通道。

使用 shape() 方法的命令如下:

>>> img = cv.imread("OpenCV_Logo.png", 1)
>>> img.shape
(222, 180, 3)

在上面的命令中:

  • 前两项 shape[0] 和 shape[1] 分别表示图像的宽度和高度。
  • Shape[2] 代表通道数。
  • 3 表示图像具有红绿蓝 (RGB) 通道。

类似地,size 属性返回图像的大小。图像大小的命令如下:

>>> img.size
119880

ndarray 中的每个元素都表示一个图像像素。

我们可以使用下面提到的命令访问和操作任何像素的值。

>>> p=img[50,50]
>>> p
array([ 1, 1, 255], dtype=uint8)

示例

以下代码将前 100X100 个像素的颜色值更改为黑色。**imshow()** 函数可以验证结果。

>>> for i in range(100):
   for j in range(100):
      img[i,j]=[0,0,0]

输出

imshow

可以使用 **split()** 函数将图像通道分割成单个平面。可以使用 **merge()** 函数合并通道。

split() 函数返回一个多通道数组。

我们可以使用以下命令分割图像通道:

>>> img = cv.imread("OpenCV_Logo.png", 1)
>>> b,g,r = cv.split(img)

您现在可以对每个平面进行操作。

假设我们将蓝色通道中的所有像素设置为 0,则代码如下:

>>> img[:, :, 0]=0
>>> cv.imshow("image", img)

生成的图像将如下所示:

Individual Planes

OpenCV Python - 位运算

位运算用于图像处理和提取图像中的重要部分。

OpenCV 中实现了以下运算符:

  • bitwise_and
  • bitwise_or
  • bitwise_xor
  • bitwise_not

示例 1

为了演示这些运算符的使用,我们使用了两个带有填充和空心圆的图像。

以下程序演示了在 OpenCV-Python 中使用位运算符:

import cv2
import numpy as np

img1 = cv2.imread('a.png')
img2 = cv2.imread('b.png')

dest1 = cv2.bitwise_and(img2, img1, mask = None)
dest2 = cv2.bitwise_or(img2, img1, mask = None)
dest3 = cv2.bitwise_xor(img1, img2, mask = None)

cv2.imshow('A', img1)
cv2.imshow('B', img2)
cv2.imshow('AND', dest1)
cv2.imshow('OR', dest2)
cv2.imshow('XOR', dest3)
cv2.imshow('NOT A', cv2.bitwise_not(img1))
cv2.imshow('NOT B', cv2.bitwise_not(img2))

if cv2.waitKey(0) & 0xff == 27:
   cv2.destroyAllWindows()

输出

Bitwise Operators Bitwise Operators Bitwise Operators

示例 2

在另一个涉及位运算的示例中,OpenCV 徽标叠加在另一张图像上。在这里,我们通过对徽标调用 **threshold()** 函数来获得掩码数组,并在它们之间执行 AND 运算。

同样,通过 NOT 运算,我们得到一个反向掩码。此外,我们还与背景图像进行 AND 运算。

以下是确定位运算使用的程序:

import cv2 as cv
import numpy as np

img1 = cv.imread('lena.jpg')
img2 = cv.imread('whitelogo.png')
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols]
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)
mask_inv = cv.bitwise_not(mask)
# Now black-out the area of logo
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv)

# Take only region of logo from logo image.
img2_fg = cv.bitwise_and(img2,img2,mask = mask)
# Put logo in ROI
dst = cv.add(img2_fg, img1_bg)
img1[0:rows, 0:cols ] = dst
cv.imshow(Result,img1)
cv.waitKey(0)
cv.destroyAllWindows()

输出

掩码图像给出以下结果:

Bitwise Operators Mask

OpenCV Python - 绘制形状和文本

在本章中,我们将学习如何使用 OpenCV-Python 在图像上绘制形状和文本。让我们首先了解如何在图像上绘制形状。

在图像上绘制形状

我们需要了解 OpenCV-Python 中所需的函数,这些函数可以帮助我们在图像上绘制形状。

函数

OpenCV-Python 包(称为 cv2)包含以下函数来绘制相应的形状。

函数 描述 命令
cv2.line() 绘制连接两点的线段。 cv2.line(img, pt1, pt2, color, thickness)
cv2.circle() 在给定点为中心的给定半径处绘制一个圆。 cv2.circle(img, center, radius, color, thickness)
cv2.rectangle 绘制一个矩形,其点为左上角和右下角。 cv2.rectangle(img, pt1, pt2, color, thickness)
cv2.ellipse() 绘制简单的或粗的椭圆弧或填充椭圆扇区。 cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color, thickness)

参数

上述函数的常用参数如下:

序号 函数和描述
1

img

要在其中绘制形状的图像

2

color

形状的颜色。对于 BGR,将其作为元组传递。对于灰度,只需传递标量值。

3

thickness

线或圆等的粗细。如果为封闭图形(如圆)传递 -1,则将填充形状。

4

lineType

线的类型,是 8 连通线、抗锯齿线等。

示例

以下示例显示如何在图像顶部绘制形状。相应的程序如下:

import numpy as np
import cv2
img = cv2.imread('LENA.JPG',1)
cv2.line(img,(20,400),(400,20),(255,255,255),3)
cv2.rectangle(img,(200,100),(400,400),(0,255,0),5)
cv2.circle(img,(80,80), 55, (255,255,0), -1)
cv2.ellipse(img, (300,425), (80, 20), 5, 0, 360, (0,0,255), -1)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出

Draw Shapes

绘制文本

提供 **cv2.putText()** 函数用于在图像上写入文本。相应的命令如下:

img, text, org, fontFace, fontScale, color, thickness)

字体

OpenCV 支持以下字体:

字体名称 字体大小
FONT_HERSHEY_SIMPLEX 0
FONT_HERSHEY_PLAIN 1
FONT_HERSHEY_DUPLEX 2
FONT_HERSHEY_COMPLEX 3
FONT_HERSHEY_TRIPLEX 4
FONT_HERSHEY_COMPLEX_SMALL 5
FONT_HERSHEY_SCRIPT_SIMPLEX 6
FONT_HERSHEY_SCRIPT_COMPLEX 7
FONT_ITALIC 16

示例

以下程序将文字标题添加到著名足球运动员莱昂内尔·梅西的照片中。

import numpy as np
import cv2
img = cv2.imread('messi.JPG',1)
txt="Lionel Messi"
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,txt,(10,100), font, 2,(255,255,255),2,cv2.LINE_AA)

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

输出

Draw Text

OpenCV Python - 处理鼠标事件

OpenCV 能够使用回调函数注册各种与鼠标相关的事件。这样做是为了根据鼠标事件的类型启动某些用户定义的操作。

序号 鼠标事件和描述
1

cv.EVENT_MOUSEMOVE

当鼠标指针已移到窗口上时。

2

cv.EVENT_LBUTTONDOWN

表示左鼠标按钮已按下。

3

cv.EVENT_RBUTTONDOWN

表示右鼠标按钮已按下。

4

cv.EVENT_MBUTTONDOWN

表示鼠标中键按下。

5

cv.EVENT_LBUTTONUP

鼠标左键释放时。

6

cv.EVENT_RBUTTONUP

鼠标右键释放时。

7

cv.EVENT_MBUTTONUP

表示鼠标中键释放。

8

cv.EVENT_LBUTTONDBLCLK

鼠标左键双击时发生此事件。

9

cv.EVENT_RBUTTONDBLCLK

表示鼠标右键双击。

10

cv.EVENT_MBUTTONDBLCLK

表示鼠标中键双击。

11

cv.EVENT_MOUSEWHEEL

向前滚动为正,向后滚动为负。

要在鼠标事件上触发函数,必须借助setMouseCallback()函数注册。相应的命令如下:

cv2.setMouseCallback(window, callbak_function)

此函数将事件的类型和位置传递给回调函数以进行进一步处理。

示例 1

以下代码在显示图像作为背景的窗口上发生左键双击事件时绘制一个圆:

import numpy as np
import cv2 as cv
# mouse callback function
def drawfunction(event,x,y,flags,param):
   if event == cv.EVENT_LBUTTONDBLCLK:
      cv.circle(img,(x,y),20,(255,255,255),-1)
img = cv.imread('lena.jpg')
cv.namedWindow('image')
cv.setMouseCallback('image',drawfunction)
while(1):
   cv.imshow('image',img)
   key=cv.waitKey(1)
   if key == 27:
      break
cv.destroyAllWindows()

输出

运行上述程序并在随机位置双击。将出现类似的输出:

Mouse Events

示例 2

以下程序根据用户输入(1、2或3)交互式地绘制矩形、线条或圆:

import numpy as np
import cv2 as cv
# mouse callback function

drawing=True
shape='r'

def draw_circle(event,x,y,flags,param):
   global x1,x2
   if event == cv.EVENT_LBUTTONDOWN:
      drawing = True
      x1,x2 = x,y
   elif event == cv.EVENT_LBUTTONUP:
      drawing = False
      if shape == 'r':
         cv.rectangle(img,(x1,x2),(x,y),(0,255,0),-1)
      if shape == 'l':
         cv.line(img,(x1,x2),(x,y),(255,255,255),3)
      if shape=='c':
         cv.circle(img,(x,y), 10, (255,255,0), -1)
img = cv.imread('lena.jpg')
cv.namedWindow('image')
cv.setMouseCallback('image',draw_circle)
while(1):
   cv.imshow('image',img)
   key=cv.waitKey(1)
   if key==ord('1'):
      shape='r'
   if key==ord('2'):
      shape='l'
   if key==ord('3'):
      shape='c'

   #print (shape)
   if key == 27:
      break
   cv.destroyAllWindows()

如果按下“1”,则在窗口表面上在鼠标左键按下和抬起的坐标之间绘制一个矩形。

如果用户选择2,则使用坐标作为端点绘制一条线。

选择3绘制圆形时,它将在鼠标抬起事件的坐标处绘制。

成功执行上述程序后,输出图像如下:

Mouse Event

OpenCV Python - 添加轨迹条

OpenCV中的轨迹条是一个滑块控件,它通过手动在条上滑动标签来帮助从连续范围内选择变量的值。标签的位置与值同步。

createTrackbar()函数使用以下命令创建一个轨迹条对象:

cv2.createTrackbar(trackbarname, winname, value, count, TrackbarCallback)

在下面的示例中,提供了三个轨迹条供用户从灰度范围0到255设置R、G和B的值。

使用轨迹条位置值,绘制一个填充颜色对应于RGB颜色的矩形。

示例

以下程序用于添加轨迹条:

import numpy as np
import cv2 as cv
img = np.zeros((300,400,3), np.uint8)
cv.namedWindow('image')
def nothing(x):
   pass

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

while(1):
   cv.imshow('image',img)
   k = cv.waitKey(1) & 0xFF
   if k == 27:
      break
   # get current positions of four trackbars
   r = cv.getTrackbarPos('R','image')
   g = cv.getTrackbarPos('G','image')
   b = cv.getTrackbarPos('B','image')

   #s = cv.getTrackbarPos(switch,'image')
   #img[:] = [b,g,r]
   cv.rectangle(img, (100,100),(200,200), (b,g,r),-1)
   cv.destroyAllWindows()

输出

Trackbar

OpenCV Python - 调整图像大小和旋转

本章将学习如何使用OpenCV Python调整图像大小和旋转图像。

调整图像大小

可以使用cv2.resize()函数放大或缩小图像。

resize()函数使用方法如下:

resize(src, dsize, dst, fx, fy, interpolation)

一般来说,插值是在已知数据点之间估计值的过程。

当图形数据包含间隙,但在间隙的两侧或间隙内的几个特定点上有可用数据时。插值允许我们估计间隙内的值。

在上文的resize()函数中,插值标志决定了用于计算目标图像大小的插值类型。

插值类型

插值类型如下:

  • INTER_NEAREST - 最近邻插值。

  • INTER_LINEAR - 双线性插值(默认使用)

  • INTER_AREA - 使用像素面积关系进行重采样。它是图像抽取的首选方法,但当图像缩放时,它类似于INTER_NEAREST方法。

  • INTER_CUBIC - 4x4像素邻域上的三次插值

  • INTER_LANCZOS4 - 8x8像素邻域上的Lanczos插值

首选的插值方法是cv2.INTER_AREA用于缩小和cv2.INTER_CUBIC(慢)和cv2.INTER_LINEAR用于放大。

示例

以下代码将“messi.jpg”图像的大小调整为其原始高度和宽度的一半。

import numpy as np
import cv2
img = cv2.imread('messi.JPG',1)
height, width = img.shape[:2]
res = cv2.resize(img,(int(width/2), int(height/2)), interpolation =
cv2.INTER_AREA)

cv2.imshow('image',res)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出

Resize an Image

旋转图像

OpenCV使用仿射变换函数进行图像操作,例如平移和旋转。仿射变换是一种可以表示为矩阵乘法(线性变换)后跟向量加法(平移)形式的变换。

cv2模块提供了两个函数cv2.warpAffinecv2.warpPerspective,您可以使用它们进行各种变换。cv2.warpAffine采用2x3变换矩阵,而cv2.warpPerspective采用3x3变换矩阵作为输入。

为了找到旋转的变换矩阵,OpenCV提供了一个函数cv2.getRotationMatrix2D,如下所示:

getRotationMatrix2D(center, angle, scale)

然后,我们将warpAffine函数应用于getRotationMatrix2D()函数返回的矩阵,以获得旋转后的图像。

以下程序将原始图像旋转90度,而不改变尺寸:

示例

import numpy as np
import cv2
img = cv2.imread('OpenCV_Logo.png',1)
h, w = img.shape[:2]

center = (w / 2, h / 2)
mat = cv2.getRotationMatrix2D(center, 90, 1)
rotimg = cv2.warpAffine(img, mat, (h, w))
cv2.imshow('original',img)
cv2.imshow('rotated', rotimg)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出

原始图像

Original Image

旋转后的图像

Rotated Image

OpenCV Python - 图像阈值化

在数字图像处理中,阈值化是基于像素强度阈值创建二值图像的过程。阈值化过程将前景像素与背景像素分离。

OpenCV提供函数执行简单、自适应Otsu阈值化。

在简单阈值化中,所有值小于阈值的像素都设置为零,其余设置为最大像素值。这是最简单的阈值化形式。

cv2.threshold()函数具有以下定义。

cv2.threshold((src, thresh, maxval, type, dst)

参数

图像阈值化的参数如下:

  • Src:输入数组。
  • Dst:相同大小的输出数组。
  • Thresh:阈值。
  • Maxval:最大值。
  • Type:阈值类型。

阈值类型

其他阈值类型如下所示:

序号 类型和函数
1

cv.THRESH_BINARY

如果src(x,y)>thresh,则dst(x,y) = maxval,否则为0

2

cv.THRESH_BINARY_INV

如果src(x,y)>thresh,则dst(x,y)=0,否则为maxval

3

cv.THRESH_TRUNC

如果src(x,y)>thresh,则dst(x,y)=threshold,否则为src(x,y)

4

cv.THRESH_TOZERO

如果src(x,y)>thresh,则dst(x,y)=src(x,y),否则为0

5

cv.THRESH_TOZERO_INV

如果src(x,y)>thresh,则dst(x,y)=0,否则为src(x,y)

这些阈值类型根据下图对输入图像进行操作:

Threshold

threshold()函数返回使用的阈值和阈值图像。

以下程序通过将阈值设置为127,从具有从255到0的灰度值的原始图像生成二值图像。

示例

使用Matplotlib库并排绘制原始图像和生成的阈值二值图像。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('gradient.png',0)
ret,img1 = cv.threshold(img,127,255,cv.THRESH_BINARY)

plt.subplot(2,3,1),plt.imshow(img,'gray',vmin=0,vmax=255)
plt.title('Original')
plt.subplot(2,3,2),plt.imshow(img1,'gray',vmin=0,vmax=255)
plt.title('Binary')
plt.show()

输出

Threshold Binary

自适应阈值根据其周围的小区域确定像素的阈值。因此,可以获得同一图像不同区域的不同阈值。这对于照明变化的图像提供了更好的结果。

cv2.adaptiveThreshold()方法采用以下输入参数:

cv.adaptiveThreshold( src, maxValue, adaptiveMethod, thresholdType, blockSize, C[, dst] )

adaptiveMethod具有以下枚举值:

  • cv.ADAPTIVE_THRESH_MEAN_C - 阈值是邻域区域的平均值减去常数C。

  • cv.ADAPTIVE_THRESH_GAUSSIAN_C - 阈值是邻域值的加权高斯和减去常数C。

示例

在下面的示例中,原始图像(messi.jpg)应用了均值和高斯自适应阈值化。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('messi.jpg',0)
img = cv.medianBlur(img,5)
th1 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_MEAN_C,\
   cv.THRESH_BINARY,11,2)
th2 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,\
   cv.THRESH_BINARY,11,2)
titles = ['Original', 'Mean Thresholding', 'Gaussian Thresholding']
images = [img, th1, th2]
for i in range(3):
   plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
   plt.title(titles[i])
   plt.xticks([]),plt.yticks([])
plt.show()

输出

使用matplotlib绘制原始图像以及自适应阈值二值图像,如下所示:

Adaptive Threshold Binary

示例

OTSU算法从图像直方图自动确定阈值。我们需要除了THRESH-BINARY标志之外还要传递cv.THRES_OTSU标志。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('messi.jpg',0)
# global thresholding
ret1,img1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
# Otsu's thresholding
ret2,img2 = cv.threshold(img,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
plt.subplot(2,2,1),plt.imshow(img,'gray',vmin=0,vmax=255)
plt.title('Original')
plt.subplot(2,2,2),plt.imshow(img1,'gray')

plt.title('Binary')
plt.subplot(2,2,3),plt.imshow(img2,'gray')
plt.title('OTSU')
plt.show()

输出

matplotlib的绘图结果如下:

Image Histogram

OpenCV Python - 图像滤波

图像基本上是由0到255之间的二进制值表示的像素矩阵,对应于灰度值。彩色图像将是一个三维矩阵,具有对应于RGB的多个通道。

图像滤波是平均像素值以改变原始图像的阴影、亮度、对比度等的过程。

通过应用低通滤波器,我们可以去除图像中的任何噪声。高通滤波器有助于检测边缘。

OpenCV库提供cv2.filter2D()函数。它通过大小为3X3或5X5等的正方形矩阵的核对原始图像执行卷积。

卷积将核矩阵水平和垂直地滑过图像矩阵。对于每个位置,添加核下所有像素,取核下像素的平均值,并用平均值替换中心像素。

对所有像素执行此操作以获得输出图像像素矩阵。请参考下图:

Pixel Matrix

cv2.filter2D()函数需要输入数组、核矩阵和输出数组参数。

示例

下图使用此函数获得作为二维卷积结果的平均图像。相应的程序如下:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('opencv_logo_gs.png')
kernel = np.ones((3,3),np.float32)/9
dst = cv.filter2D(img,-1,kernel)
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(dst),plt.title('Convolved')
plt.xticks([]), plt.yticks([])
plt.show()

输出

Pixel Matrix

滤波函数类型

OpenCV中的其他滤波函数类型包括:

  • BilateralFilter - 减少不需要的噪声,同时保持边缘完整。

  • BoxFilter - 这是一个平均模糊操作。

  • GaussianBlur - 消除高频内容,如噪声和边缘。

  • MedianBlur - 它不取平均值,而是取核下所有像素的中值,并替换中心值。

OpenCV Python - 边缘检测

这里的边缘是指图像中物体的边界。OpenCV有一个cv2.Canny()函数,通过实现Canny算法来识别图像中各种物体的边缘。

Canny边缘检测算法由John Canny开发。根据它,物体的边缘是通过执行以下步骤确定的:

第一步是减少图像中的噪声像素。这是通过应用5X5高斯滤波器来完成的。

第二步涉及查找图像的强度梯度。通过应用Sobel算子获得水平和垂直方向的一阶导数(Gx和Gy)来过滤第一阶段的平滑图像。

均方根值给出边缘梯度,导数的反正切比给出边缘的方向。

$$ \mathrm{Edge \:gradient\:G\:=\:\sqrt{G_x^2+G_y^2}} $$

$$ \mathrm{Angle\:\theta\:=\:\tan^{-1}(\frac{G_{y}}{G_{x}}) $$

获得梯度幅度和方向后,将对图像进行全扫描以去除任何可能不构成边缘的不需要的像素。

下一步是使用minval和maxval阈值执行滞后阈值化。小于minval和maxval的强度梯度是非边缘,因此被丢弃。介于两者之间的根据其连通性被视为边缘点或非边缘点。

所有这些步骤都由OpenCV的cv2.Canny()函数执行,该函数需要输入图像数组和minval和maxval参数。

示例

这是一个Canny边缘检测的示例。程序如下:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('lena.jpg', 0)
edges = cv.Canny(img,100,200)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edges of original Image'), plt.xticks([]), plt.yticks([])
plt.show()

输出

Canny Edge

OpenCV Python - 直方图

直方图显示图像中的强度分布。它在X轴上绘制像素值(0到255),在Y轴上绘制像素数量。

通过使用直方图,可以了解指定图像的对比度、亮度和强度分布。直方图中的bin表示X轴上值的增量部分。

在我们的例子中,它是像素值,默认bin大小为1。

在OpenCV库中,函数**cv2.calcHist()**计算输入图像的直方图。函数命令如下:

cv.calcHist(images, channels, mask, histSize, ranges)

参数

**cv2.calcHist()**函数的参数如下:

  • **images** - 它是uint8或float32类型的源图像,用方括号括起来,即“[img]”。

  • **channels** - 它是我们计算直方图的通道索引。对于灰度图像,其值为[0]。对于BGR图像,您可以传递[0]、[1]或[2]来计算每个通道的直方图。

  • **mask** - 对于全图,掩码图像设置为“None”。对于图像的特定区域,您必须为此创建一个掩码图像并将其作为掩码。

  • **histSize** - 这表示我们的BIN数量。

  • **ranges** - 通常是[0,256]。

使用Matplotlib绘制直方图

直方图可以使用Matplotlib的**pyplot.plot()**函数或调用OpenCV库中的**Polylines()**函数获得。

示例

下面的程序计算图像(lena.jpg)中每个通道的直方图,并绘制每个通道的强度分布:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('lena.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
   hist = cv.calcHist([img],[i],None,[256],[0,256])
   plt.plot(hist, color = col)
   plt.xlim([0,256])
plt.show()

输出

Histogram

OpenCV Python - 颜色空间

颜色空间是一个描述如何表示颜色的数学模型。它在一个特定、可测量和固定的可能的颜色和亮度值范围内进行描述。

OpenCV支持以下常用的颜色空间:

  • **RGB颜色空间** - 这是一个加色颜色空间。颜色值是通过红色、绿色和蓝色颜色值的组合获得的。每个值由一个介于0到255之间的数字表示。

  • **HSV颜色空间** - H、S和V分别代表色相、饱和度和值。这是RGB的替代颜色模型。该模型应该更接近人眼感知任何颜色的方式。色相值介于0到179之间,而S和V值介于0到255之间。

  • **CMYK颜色空间** - 与RGB相反,CMYK是一种减色颜色模型。字母分别代表青色、品红色、黄色和黑色。白光减去红色得到青色,绿色从白色中减去得到品红色,白色减去蓝色得到黄色。所有值都在0到100%的范围内表示。

  • **CIELAB颜色空间** - LAB颜色空间具有三个分量:L表示亮度,A表示从绿色到品红色的颜色分量,B表示从蓝色到黄色的分量。

  • **YCrCb颜色空间** - 这里,Cr代表R-Y,Cb代表B-Y。这有助于将亮度与色度分离到不同的通道中。

OpenCV使用**cv2.cvtColor()**函数支持图像在颜色空间之间的转换。

cv2.cvtColor()函数的命令如下:

cv.cvtColor(src, code, dst)

转换代码

转换由以下预定义的转换代码控制。

序号 转换代码 & 函数
1

cv.COLOR_BGR2BGRA

向RGB或BGR图像添加alpha通道。

2

cv.COLOR_BGRA2BGR

从RGB或BGR图像中删除alpha通道。

3

cv.COLOR_BGR2GRAY

在RGB/BGR和灰度之间转换。

4

cv.COLOR_BGR2YCrCb

将RGB/BGR转换为亮度-色度

5

cv.COLOR_BGR2HSV

将RGB/BGR转换为HSV

6

cv.COLOR_BGR2Lab

将RGB/BGR转换为CIE Lab

7

cv.COLOR_HSV2BGR

HSV向RGB/BGR的反向转换

示例

下面的程序显示了将具有RGB颜色空间的原始图像转换为HSV和灰度方案:

import cv2
img = cv2.imread('messi.jpg')
img1 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY )
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Displaying the image
cv2.imshow('original', img)
cv2.imshow('Gray', img1)
cv2.imshow('HSV', img2)

输出

RGB Color Space

RGB Color Space

RGB Color Space

OpenCV Python - 形态学变换

基于图像形状的简单操作称为形态学变换。两种最常见的变换是**腐蚀和膨胀**。

腐蚀

腐蚀去除前景对象的边界。类似于二维卷积,一个内核在图像A上滑动。如果内核下的所有像素都是1,则保留原始图像中的像素。

否则将其设为0,从而导致腐蚀。边界附近的所有像素都被丢弃。此过程用于去除白噪声。

OpenCV中**erode()**函数的命令如下:

cv.erode(src, kernel, dst, anchor, iterations)

参数

OpenCV中的**erode()**函数使用以下参数:

src和dst参数是相同大小的输入和输出图像数组。内核是用于腐蚀的结构元素矩阵。例如,3X3或5X5。

anchor参数默认为-1,这意味着锚元素位于中心。迭代次数是指应用腐蚀的次数。

膨胀

这正好与腐蚀相反。这里,如果内核下至少有一个像素为1,则像素元素为1。结果,它增加了图像中的白色区域。

dilate()函数的命令如下:

cv.dilate(src, kernel, dst, anchor, iterations)

参数

**dilate()**函数具有与erode()函数相同的参数。这两个函数都可以具有附加的可选参数,如BorderType和borderValue。

BorderType是图像边界的枚举类型(CONSTANT、REPLICATE、TRANSPERANT等)。

borderValue用于常数边界的情况。默认情况下,它是0。

示例

下面是一个示例程序,显示了erode()和dilate()函数的使用:

import cv2 as cv
import numpy as np
img = cv.imread('LinuxLogo.jpg',0)
kernel = np.ones((5,5),np.uint8)
erosion = cv.erode(img,kernel,iterations = 1)
dilation = cv.dilate(img,kernel,iterations = 1)
cv.imshow('Original', img)
cv.imshow('Erosion', erosion)
cv.imshow('Dialation', dilation)

输出

原始图像

Morphological

腐蚀

Erosion

膨胀

Dilation

OpenCV Python - 图像轮廓

轮廓是连接沿边界所有连续点的曲线,这些点具有相同的颜色或强度。轮廓对于形状分析和目标检测非常有用。

查找轮廓

在查找轮廓之前,我们应该应用阈值或Canny边缘检测。然后,通过使用**findContours()**方法,我们可以在二值图像中找到轮廓。

**findContours()**函数用法的命令如下:

cv.findContours(image, mode, method, contours)

参数

**findContours()**函数的参数如下:

  • image - 源,一个8位单通道图像。
  • mode - 轮廓检索模式。
  • method - 轮廓逼近方法。

mode参数的值如下所示:

  • **cv.RETR_EXTERNAL** - 只检索最外层的轮廓。

  • **cv.RETR_LIST** - 检索所有轮廓,而不建立任何层次关系。

  • **cv.RETR_CCOMP** - 检索所有轮廓并将它们组织成两级层次结构。

  • **cv.RETR_TREE** - 检索所有轮廓并重建嵌套轮廓的完整层次结构。

另一方面,逼近方法可以是以下之一:

  • **cv.CHAIN_APPROX_NONE** - 存储绝对所有轮廓点。

  • **cv.CHAIN_APPROX_SIMPLE** - 压缩水平、垂直和对角线段,只留下它们的端点。

绘制轮廓

检测到轮廓向量后,使用**cv.drawContours()**函数在原始图像上绘制轮廓。

cv.drawContours()函数的命令如下:

cv.drawContours(image, contours, contourIdx, color)

参数

**drawContours()**函数的参数如下:

  • image - 目标图像。
  • contours - 所有输入轮廓。每个轮廓都存储为一个点向量。
  • contourIdx - 指示要绘制的轮廓的参数。如果为负数,则绘制所有轮廓。
  • color - 轮廓的颜色。

示例

以下代码是在一个输入图像上绘制轮廓的示例,该图像具有三个填充黑色颜色的形状。

第一步,我们获得一个灰度图像,然后执行Canny边缘检测。

在结果图像上,我们然后调用findContours()函数。其结果是一个点向量。然后我们调用drawContours()函数。

完整的代码如下:

import cv2
import numpy as np

img = cv2.imread('shapes.png')
cv2.imshow('Original', img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

canny = cv2.Canny(gray, 30, 200)

contours, hierarchy = cv2.findContours(canny,
   cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
print("Number of Contours = " ,len(contours))
cv2.imshow('Canny Edges', canny)

cv2.drawContours(img, contours, -1, (0, 255, 0), 3)

cv2.imshow('Contours', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出

原始图像、Canny边缘检测后的图像以及绘制轮廓后的图像将分别显示在单独的窗口中,如下所示:

separate windows

**Canny边缘检测**后,图像如下:

canny edge detection

**绘制轮廓**后,图像如下:

contours are drawn

OpenCV Python - 模板匹配

模板匹配技术用于检测图像中与样本或模板图像匹配的一个或多个区域。

OpenCV中定义了**Cv.matchTemplate()**函数,其命令如下:

cv.matchTemplate(image, templ, method)

其中image是需要查找templ(模板)模式的输入图像。method参数采用以下值之一:

  • cv.TM_CCOEFF,
  • cv.TM_CCOEFF_NORMED, cv.TM_CCORR,
  • cv.TM_CCORR_NORMED,
  • cv.TM_SQDIFF,
  • cv.TM_SQDIFF_NORMED

此方法将模板图像滑过输入图像。这类似于卷积的过程,并比较模板和模板图像下输入图像的图像块。

它返回一个灰度图像,其每个像素表示它与模板的匹配程度。如果输入图像的大小为(WxH),模板图像的大小为(wxh),则输出图像的大小将为(W-w+1, H-h+1)。因此,该矩形是您的模板区域。

示例

在下面的示例中,使用一张印有印度板球运动员Virat Kohli面部的图像作为模板,与另一张描绘他和另一位印度板球运动员M.S.Dhoni照片的图像进行匹配。

下面的程序使用80%的阈值,并在匹配的面部周围绘制一个矩形:

import cv2
import numpy as np

img = cv2.imread('Dhoni-and-Virat.jpg',1)
cv2.imshow('Original',img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

template = cv2.imread('virat.jpg',0)
cv2.imshow('Template',template)
w,h = template.shape[0], template.shape[1]

matched = cv2.matchTemplate(gray,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.8

loc = np.where( matched >= threshold)

for pt in zip(*loc[::-1]):
   cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0,255,255), 2)

cv2.imshow('Matched with Template',img)

输出

原始图像、模板和匹配结果图像如下:

原始图像

Template Matching

**模板**如下:

Templates

**与模板匹配**后的图像如下:

Matched Templates

OpenCV Python - 图像金字塔

有时,我们可能需要将图像转换为与其原始大小不同的尺寸。为此,您可以放大图像(放大)或缩小图像(缩小)。

图像金字塔是由多个图像组成的集合(由单个原始图像构建),这些图像被连续下采样指定的次数。

高斯金字塔用于对图像进行下采样,而拉普拉斯金字塔则从金字塔中分辨率较低的图像重建上采样图像。

将金字塔视为一组层。图像如下所示:

Pyramid

金字塔较高层的图像尺寸较小。为了生成高斯金字塔中下一层的图像,我们将较低层图像与高斯内核进行卷积。

现在移除所有偶数行和偶数列。生成的图像面积将是其前身图像的 1/4。对原始图像迭代此过程会生成整个图像金字塔。

为了使图像更大,用零填充列。首先,将图像在每个维度上放大到原始图像的两倍,用零填充新的偶数行,然后使用卷积核对缺失像素的值进行近似。

cv.pyrUp() 函数将原始图像大小加倍,而 cv.pyrDown() 函数将其缩小一半。

示例

下面的程序根据用户输入的“I”或“o”分别调用 pyrUp() 和 pyrDown() 函数。

请注意,当我们减小图像大小时,图像信息会丢失。一旦我们缩小图像,如果我们将其重新缩放为原始大小,我们将丢失一些信息,并且新图像的分辨率将远低于原始图像。

import sys
import cv2 as cv

filename = 'chicky_512.png'

src = cv.imread(filename)

while 1:
   print ("press 'i' for zoom in 'o' for zoom out esc to stop")
   rows, cols, _channels = map(int, src.shape)
   cv.imshow('Pyramids', src)
   k = cv.waitKey(0)

   if k == 27:
      break

   elif chr(k) == 'i':
      src = cv.pyrUp(src, dstsize=(2 * cols, 2 * rows))

   elif chr(k) == 'o':
      src = cv.pyrDown(src, dstsize=(cols // 2, rows // 2))

cv.destroyAllWindows()

输出

Image Pyramids

Gaussian Pyramids

Laplacian Pyramids

OpenCV Python - 图像加法

当图像被 imread() 函数读取时,生成的图像对象实际上是一个二维或三维矩阵,这取决于图像是灰度图像还是 RGB 图像。

因此,cv2.add() 函数将两个图像矩阵相加并返回另一个图像矩阵。

示例

下面的代码读取两张图像并执行它们的二元加法:

kalam = cv2.imread('kalam.jpg')
einst = cv2.imread('einstein.jpg')
img = cv2.add(kalam, einst)
cv2.imshow('addition', img)

结果

Image Addition

OpenCV 除了线性二元加法之外,还有一个 addWeighted() 函数可以执行两个数组的加权和。该函数的命令如下:

Cv2.addWeighted(src1, alpha, src2, beta, gamma)

参数

addWeighted() 函数的参数如下:

  • src1 − 第一个输入数组。
  • alpha − 第一个数组元素的权重。
  • src2 − 第二个输入数组,大小和通道数与第一个相同。
  • beta − 第二个数组元素的权重。
  • gamma − 加到每个和上的标量。

此函数根据以下等式添加图像:

$$\mathrm{g(x)=(1-\alpha)f_{0}(x)+\alpha f_{1}(x)}$$

上述示例中获得的图像矩阵用于执行加权和。

通过将 α 从 0 更改为 1,可以实现从一个图像到另一个图像的平滑过渡,从而使它们融合在一起。

第一张图像的权重为 0.3,第二张图像的权重为 0.7。伽马因子取为 0。

addWeighted() 函数的命令如下:

img = cv2.addWeighted(kalam, 0.3, einst, 0.7, 0)

可以看出,与二元加法相比,图像加法更平滑。

Gamma Factor

OpenCV Python - 使用图像金字塔进行图像混合

可以使用图像金字塔来最大限度地减少图像的不连续性。这将产生无缝混合的图像。

采取以下步骤以获得最终结果:

首先加载图像并为两者找到高斯金字塔。相应的程序如下:

import cv2
import numpy as np,sys

kalam = cv2.imread('kalam.jpg')
einst = cv2.imread('einstein.jpg')
### generate Gaussian pyramid for first
G = kalam.copy()
gpk = [G]
for i in range(6):
   G = cv2.pyrDown(G)
   gpk.append(G)
# generate Gaussian pyramid for second
G = einst.copy()
gpe = [G]
for i in range(6):
   G = cv2.pyrDown(G)
   gpe.append(G)

从高斯金字塔中获得各自的拉普拉斯金字塔。相应的程序如下:

# generate Laplacian Pyramid for first
lpk = [gpk[5]]
for i in range(5,0,-1):
   GE = cv2.pyrUp(gpk[i])
   L = cv2.subtract(gpk[i-1],GE)
   lpk.append(L)

# generate Laplacian Pyramid for second
lpe = [gpe[5]]
for i in range(5,0,-1):
   GE = cv2.pyrUp(gpe[i])
   L = cv2.subtract(gpe[i-1],GE)
   lpe.append(L)

然后,将第一张图像的左半部分与第二张图像的右半部分连接到金字塔的每个级别。相应的程序如下:

# Now add left and right halves of images in each level
LS = []
for la,lb in zip(lpk,lpe):
   rows,cols,dpt = la.shape
   ls = np.hstack((la[:,0:int(cols/2)], lb[:,int(cols/2):]))
   LS.append(ls)

最后,从此联合金字塔重建图像。相应的程序如下:

ls_ = LS[0]
for i in range(1,6):
   ls_ = cv2.pyrUp(ls_)
   ls_ = cv2.add(ls_, LS[i])
   cv2.imshow('RESULT',ls_)

输出

混合结果应如下所示:

Blending Pyramids

OpenCV Python - 傅里叶变换

傅里叶变换用于将图像从其空间域变换到其频域,方法是将其分解为正弦和余弦分量。

对于数字图像,基本的灰度图像值通常在 0 和 255 之间。因此,傅里叶变换也需要是离散傅里叶变换 (DFT)。它用于查找频域。

在数学上,二维图像的傅里叶变换表示如下:

$$\mathrm{F(k,l)=\displaystyle\sum\limits_{i=0}^{N-1}\: \displaystyle\sum\limits_{j=0}^{N-1} f(i,j)\:e^{-i2\pi (\frac{ki}{N},\frac{lj}{N})}}$$

如果幅度在短时间内变化很快,则可以说它是高频信号。如果它变化缓慢,则它是低频信号。

对于图像,幅度在边缘点或噪声处急剧变化。因此,边缘和噪声是图像中的高频内容。如果幅度变化不大,则它是低频分量。

OpenCV 提供了cv.dft()cv.idft() 函数来实现此目的。

cv.dft() 执行一维或二维浮点数组的离散傅里叶变换。相应的命令如下:

cv.dft(src, dst, flags)

这里:

  • src − 可以是实数或复数的输入数组。
  • dst − 输出数组,其大小和类型取决于标志。
  • flags − 变换标志,表示 DftFlags 的组合。

cv.idft() 计算一维或二维数组的逆离散傅里叶变换。相应的命令如下:

cv.idft(src, dst, flags)

为了获得离散傅里叶变换,输入图像被转换为 np.float32 数据类型。然后使用获得的变换将零频率分量移到频谱的中心,从中计算幅度谱。

示例

下面是使用 Matplotlib 的程序,我们绘制原始图像和幅度谱:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('lena.jpg',0)
dft = cv.dft(np.float32(img),flags = cv.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
magnitude_spectrum = 20*np.log(cv.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

输出

Fourier Transform

OpenCV Python - 从摄像头捕捉视频

通过使用 OpenCV 库中的 VideoCapture() 函数,可以很容易地在 OpenCV 窗口上从摄像头捕捉实时流。

此函数需要设备索引作为参数。您的计算机可能连接了多个摄像头。它们由从内置网络摄像头开始的索引 0 开始的索引枚举。该函数返回一个 VideoCapture 对象。

cam = cv.VideoCapture(0)

打开摄像头后,我们可以借助 read() 函数读取连续的帧。

ret,frame = cam.read()

read() 函数读取下一个可用帧和一个返回值 (True/False)。此帧现在使用 cvtColor() 函数在所需的色彩空间中呈现,并在 OpenCV 窗口中显示。

img = cv.cvtColor(frame, cv.COLOR_BGR2RGB)
# Display the resulting frame
cv.imshow('frame', img)

要将当前帧捕获到图像文件,可以使用 imwrite() 函数。

cv2.imwrite(“capture.png”, img)

要将来自摄像头的实时流保存到视频文件,OpenCV 提供了 VideoWriter() 函数。

cv.VideoWriter( filename, fourcc, fps, frameSize)

fourcc 参数是视频编解码器的标准化代码。OpenCV 支持各种编解码器,例如 DIVX、XVID、MJPG、X264 等。fps 和 framesize 参数取决于视频捕获设备。

VideoWriter() 函数返回一个 VideoWrite 流对象,捕获的帧将依次写入循环中。最后,释放帧和 VideoWriter 对象以完成视频的创建。

示例

以下示例读取内置网络摄像头的实时视频,并将其保存到 ouput.avi 文件中。

import cv2 as cv
cam = cv.VideoCapture(0)
cc = cv.VideoWriter_fourcc(*'XVID')
file = cv.VideoWriter('output.avi', cc, 15.0, (640, 480))
if not cam.isOpened():
   print("error opening camera")
   exit()
while True:
   # Capture frame-by-frame
   ret, frame = cam.read()
   # if frame is read correctly ret is True
   if not ret:
      print("error in retrieving frame")
      break
   img = cv.cvtColor(frame, cv.COLOR_BGR2RGB)
   cv.imshow('frame', img)
   file.write(img)

   
   if cv.waitKey(1) == ord('q'):
      break

cam.release()
file.release()
cv.destroyAllWindows()

OpenCV Python - 从文件中播放视频

VideoCapture() 函数也可以从视频文件而不是摄像头检索帧。因此,我们只需将摄像头索引替换为要在 OpenCV 窗口上播放的视频文件的名称即可。

video=cv2.VideoCapture(file)

虽然这足以开始渲染视频文件,但如果视频文件包含声音,则声音将不会一起播放。为此,您需要安装 ffpyplayer 模块。

FFPyPlayer

FFPyPlayer 是 FFmpeg 库的 Python 绑定,用于播放和写入媒体文件。要安装,请使用以下命令使用 pip 安装程序实用程序。

pip3 install ffpyplayer

此模块中 MediaPlayer 对象的 get_frame() 方法返回音频帧,该音频帧将与从视频文件读取的每个帧一起播放。

以下是播放包含音频的视频文件的完整代码:

import cv2

from ffpyplayer.player import MediaPlayer
file="video.mp4"

video=cv2.VideoCapture(file)
player = MediaPlayer(file)
while True:
   ret, frame=video.read()
   audio_frame, val = player.get_frame()
   if not ret:
      print("End of video")
      break
   if cv2.waitKey(1) == ord("q"):
      break
   cv2.imshow("Video", frame)
   if val != 'eof' and audio_frame is not None:
      #audio
      img, t = audio_frame
video.release()
cv2.destroyAllWindows()

OpenCV Python - 从视频中提取图像

视频只不过是一系列帧,每帧都是一幅图像。使用 OpenCV,可以通过执行 imwrite() 函数到视频结尾来提取构成视频文件的所有帧。

cv2.read() 函数返回下一个可用帧。该函数还提供一个返回值,该返回值在流结束前继续为 true。在这里,计数器在循环内递增,并用作文件名。

以下程序演示如何从视频中提取图像:

import cv2
import os

cam = cv2.VideoCapture("video.avi")

frameno = 0
while(True):
   ret,frame = cam.read()
   if ret:
      # if video is still left continue creating images
      name = str(frameno) + '.jpg'
      print ('new frame captured...' + name)

      cv2.imwrite(name, frame)
      frameno += 1
   else:
      break

cam.release()
cv2.destroyAllWindows()

OpenCV Python - 从图像生成视频

在上一章中,我们使用了 VideoWriter() 函数将来自摄像头的实时流保存为视频文件。为了将多个图像拼接成视频,我们将使用相同的函数。

首先,确保所有必需的图像都在一个文件夹中。Python 的内置 glob 模块中的 glob() 函数构建一个图像数组,以便我们可以迭代它。

从文件夹中的图像读取图像对象,并将其附加到图像数组。

以下程序解释如何将多个图像拼接成视频:

import cv2
import numpy as np
import glob

img_array = []
for filename in glob.glob('*.png'):
   img = cv2.imread(filename)
   height, width, layers = img.shape
   size = (width,height)
   img_array.append(img)

使用 VideoWriter() 函数创建一个视频流,将图像数组的内容写入其中。以下是相应的程序:

out = cv2.VideoWriter('video.avi',cv2.VideoWriter_fourcc(*'DIVX'), 15, size)

for i in range(len(img_array)):
   out.write(img_array[i])
out.release()

您应该在当前文件夹中找到名为 ‘video.avi’ 的文件。

OpenCV Python - 人脸检测

OpenCV 使用基于 Haar 特征的级联分类器进行目标检测。这是一种基于机器学习的算法,其中级联函数是从大量正图像和负图像训练出来的。然后将其用于检测其他图像中的对象。该算法使用级联分类器的概念。

可以从 https://github.com下载人脸、眼睛等的预训练分类器。

对于以下示例,请从此 URL 下载并复制 haarcascade_frontalface_default.xmlhaarcascade_eye.xml。然后,加载我们的输入图像,以灰度模式用于人脸检测。

CascadeClassifier 类的 DetectMultiScale() 方法检测输入图像中的对象。它以矩形及其尺寸 (x,y,w,h) 的形式返回检测到的人脸的位置。一旦我们获得这些位置,我们就可以将其用于眼睛检测,因为眼睛总是在脸上!

示例

人脸检测的完整代码如下:

import numpy as np
import cv2

face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')

img = cv2.imread('Dhoni-and-virat.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
   img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
   roi_gray = gray[y:y+h, x:x+w]
   roi_color = img[y:y+h, x:x+w]
   eyes = eye_cascade.detectMultiScale(roi_gray)
   for (ex,ey,ew,eh) in eyes:
      cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)

cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出

您将在输入图像中的人脸周围绘制矩形,如下所示:

Face Detection

OpenCV Python - 均值漂移和 CamShift

在本章中,让我们学习 OpenCV-Python 中的均值漂移和 CamShift。首先,让我们了解什么是均值漂移。

均值漂移

均值漂移算法识别数据集中具有高数据点浓度或聚类的位置。该算法在每个数据点放置一个内核,并将它们加在一起以构成内核密度估计 (KDE)

KDE 将分别具有高数据点密度和低数据点密度的位置。均值漂移是一种非常有用的方法,可以跟踪视频中特定对象。

对视频的每一帧都进行像素分布检查。初始感兴趣区域(ROI)通常是正方形或圆形。为此,位置是通过硬编码指定的,并识别像素分布最大区域。

随着视频播放,ROI窗口向像素分布最大区域移动。移动方向取决于跟踪窗口中心与其内部所有k个像素的质心之间的差异。

为了在OpenCV中使用Meanshift,首先找到目标的直方图(其中只考虑色调),并将其目标反投影到每一帧中以进行Meanshift计算。我们还需要提供ROI窗口的初始位置。

我们重复计算直方图的反投影并计算Meanshift以获得跟踪窗口的新位置。之后,我们使用其尺寸在帧上绘制一个矩形。

函数

程序中使用的OpenCV函数如下:

  • cv.calcBackProject() − 计算直方图的反投影。

  • cv.meanShift() − 使用初始搜索窗口和迭代搜索算法的停止条件来进行目标直方图的反投影。

示例

以下是Meanshift的示例程序:

import numpy as np
import cv2 as cv

cap = cv.VideoCapture('traffic.mp4')

ret,frame = cap.read()

# dimensions of initial location of window
x, y, w, h = 300, 200, 100, 50
tracker = (x, y, w, h)

region = frame[y:y+h, x:x+w]
hsv_reg = cv.cvtColor(region, cv.COLOR_BGR2HSV)
mask = cv.inRange(hsv_reg, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
reg_hist = cv.calcHist([hsv_reg],[0],mask,[180],[0,180])
cv.normalize(reg_hist,reg_hist,0,255,cv.NORM_MINMAX)

# Setup the termination criteria
criteria = ( cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1 )

while(1):
   ret, frame = cap.read()

   if ret == True:
      hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
      dst = cv.calcBackProject([hsv],[0],reg_hist,[0,180],1)

      # apply meanshift
      ret, tracker = cv.meanShift(dst, tracker, criteria)

      # Draw it on image
      x,y,w,h = tracker
      img = cv.rectangle(frame, (x,y), (x+w,y+h), 255,2)
      cv.imshow('img',img)

      k = cv.waitKey(30) & 0xff
      if k==115:
         cv.imwrite('capture.png', img)
      if k == 27:
         break

程序运行时,Meanshift算法将我们的窗口移动到密度最大的新位置。

输出

以下是移动窗口的快照:

Meanshift

CamShift

Meanshift算法的一个缺点是,跟踪窗口的大小保持不变,而不管物体与摄像机的距离如何。此外,只有当物体在该物体的区域内时,窗口才会跟踪该物体。因此,我们必须手动硬编码窗口,并且必须小心地进行。

CAMshift(代表Continuously Adaptive Meanshift,连续自适应均值漂移)解决了这些问题。一旦meanshift收敛,CamShift算法就会更新窗口的大小,以便跟踪窗口可以改变大小甚至旋转,以更好地与被跟踪物体的运动相关联。

在下面的代码中,使用了camshift()函数而不是meanshift()函数。

首先,它使用meanShift查找物体中心,然后调整窗口大小并找到最佳旋转。该函数返回物体的位姿、大小和方向。位置使用polylines()绘图函数在帧上绘制。

示例

在之前的程序中,将Meanshift()函数替换为CamShift()函数,如下所示:

# apply camshift
ret, tracker = cv.CamShift(dst, tracker, criteria)
pts = cv.boxPoints(ret)
pts = np.int0(pts)
img = cv.polylines(frame,[pts],True, 255,2)
cv.imshow('img',img)

输出

以下是修改后的程序结果的快照,显示了跟踪窗口的旋转矩形:

Camshift

OpenCV Python - 特征检测

在图像处理的背景下,特征是图像中关键区域的数学表示。它们是图像视觉内容的矢量表示。

特征使得对它们进行数学运算成为可能。各种计算机视觉应用包括目标检测、运动估计、分割、图像对齐等。

任何图像中的显著特征包括边缘、角点或图像的一部分。OpenCV支持Haris角点检测Shi-Tomasi角点检测算法。OpenCV库还提供实现SIFT(尺度不变特征变换)、SURF(加速鲁棒特征)和FAST角点检测算法的功能。

Harris和Shi-Tomasi算法是旋转不变的。即使图像旋转,我们也可以找到相同的角点。但是,当图像放大时,如果图像的角点不再是角点,则可能不会是角点。下图描述了这一点。

Shi Tomasi

D. Lowe的新算法,尺度不变特征变换(SIFT)提取关键点并计算其描述符。

这是通过以下步骤实现的:

  • 尺度空间极值检测。
  • 关键点定位。
  • 方向分配。
  • 关键点描述符。
  • 关键点匹配。

就OpenCV中SIFT的实现而言,它从加载图像并将其转换为灰度图像开始。cv.SHIFT_create()函数创建一个SIFT对象。

示例

调用其detect()方法可以获得关键点,这些关键点绘制在原始图像的顶部。下面的代码实现了此过程

import numpy as np
import cv2 as cv
img = cv.imread('home.jpg')
gray= cv.cvtColor(img,cv.COLOR_BGR2GRAY)
sift = cv.SIFT_create()
kp = sift.detect(gray,None)
img=cv.drawKeypoints(gray,kp,img)
cv.imwrite('keypoints.jpg',img)

输出

原始图像和绘制了关键点的图像如下所示:

这是一张原始图像

Scale Space

下图是带有关键点的图像

SIFT

OpenCV Python - 特征匹配

OpenCV提供了两种特征匹配技术:暴力匹配和FLANN匹配器技术。

示例

以下示例使用暴力方法

import numpy as np
import cv2

img1 = cv2.imread('lena.jpg')
img2 = cv2.imread('lena-test.jpg')

# Convert it to grayscale
img1_bw = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
img2_bw = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

orb = cv2.ORB_create()

queryKeypoints, queryDescriptors = orb.detectAndCompute(img1_bw,None)
trainKeypoints, trainDescriptors = orb.detectAndCompute(img2_bw,None)

matcher = cv2.BFMatcher()
matches = matcher.match(queryDescriptors,trainDescriptors)

img = cv2.drawMatches(img1, queryKeypoints,
img2, trainKeypoints, matches[:20],None)

img = cv2.resize(img, (1000,650))

cv2.imshow("Feature Match", img)

输出

Feature Matching

OpenCV Python - 使用KNN进行数字识别

KNN代表K近邻,是一种基于监督学习的机器学习算法。它试图将新的数据点放入与现有类别最相似的类别中。所有可用数据都分类到不同的类别中,新的数据点根据相似性放入其中一个类别中。

KNN算法的工作原理如下:

  • 最好选择奇数作为要检查的邻居数K。
  • 计算它们的欧几里得距离。
  • 根据计算出的欧几里得距离,取K个最近的邻居。
  • 统计每个类别中数据点的数量。
  • 数据点最多的类别就是新的数据点所属的类别。

作为使用OpenCV实现KNN算法的示例,我们将使用包含5000张手写数字图像的digits.png图像,每张图像大小为20X20像素。

KNN

首先,将此图像分成5000个数字。这是我们的特征集。将其转换为NumPy数组。程序如下:

import numpy as np
import cv2

image = cv2.imread('digits.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

fset=[]
for i in np.vsplit(gray,50):
   x=np.hsplit(i,100)
   fset.append(x)

NP_array = np.array(fset)

现在,我们将此数据分成训练集和测试集,每个大小为(2500, 20x20),如下所示:

trainset = NP_array[:,:50].reshape(-1,400).astype(np.float32)
testset = NP_array[:,50:100].reshape(-1,400).astype(np.float32)

接下来,我们必须为每个数字创建10个不同的标签,如下所示:

k = np.arange(10)
train_labels = np.repeat(k,250)[:,np.newaxis]
test_labels = np.repeat(k,250)[:,np.newaxis]

我们现在可以开始KNN分类了。创建分类器对象并训练数据。

knn = cv2.ml.KNearest_create()
knn.train(trainset, cv2.ml.ROW_SAMPLE, train_labels)

选择k值为3,获得分类器的输出。

ret, output, neighbours, distance = knn.findNearest(testset, k = 3)

将输出与测试标签进行比较,以检查分类器的性能和准确性。

该程序显示出手写数字检测的准确率为91.64%。

result = output==test_labels
correct = np.count_nonzero(result)
accuracy = (correct*100.0)/(output.size)
print(accuracy)
广告