系列内容:OpenCV概述与环境配置,OpenCV基础知识和绘制图形,图像的算数与位运算,图像视频的加载和显示,图像基本变换,滤波器,形态学,图像轮廓,图像直方图,车辆统计项目,特征检测和匹配,图像查找和拼接,虚拟计算器项目,信用卡识别项目,图像的分割与修复,人脸检测与车牌识别,目标追踪,答题卡识别判卷与文档ocr扫描识别,光流估计

(一)OpenCV的色彩空间

1.RGB和BGR

最常见的色彩空间就是RGB,人眼也是按照RGB色彩空间分别颜色OpenCV默认使用BGR,BGR与RGB色彩空间的区别在于图片在色彩通道上的排列顺序不同RGB

(1)RGB

RGB如图2.1所示:

2.1-RGB

(2)BGR

BGR如图2.2所示:

2.2-BGR

2.HSV,HSL和YUV

(1)HSV(HSB)

OpenCV使用最多的色彩空间,如图2.3所示:

2.3-HSV(HSB)
  1. Hue:色相,圆盘,0°红,120°黄,240°蓝
  2. Saturation:饱和度,饱和度越高,颜色越鲜艳
  3. Value(Brighteness):明度,颜色明亮程度,取值为0\%(黑)-100\%(白)

使用原因:便于图像处理,如根据Vue值来判断背景颜色

(2)HSL

概述:

HSL和HSV差不多

  1. Hue:色相
  2. Saturation:饱和度
  3. Lightness:亮度

HSL和HSV对比,如图2.4所示:

2.4-HSL和HSV对比

(3)YUV

概述:

YUV是一种颜色编码方式,常使用在各个视频处理组件中.

  1. Y表示明亮度(Luma)
  2. U和V表示色度(Chroma)

优势:占用少量的带宽

4:4:4表示完全取样

(4)例子

待处理的图片,如图2.5所示:

2.5-待处理的图片

读取图片:

import cv2  # 导入OpenCV库

# 读取图片文件
dog = cv2.imread('./dog.jpg')  

# 显示图片,窗口名称为'dog'
cv2.imshow('dog', dog)  

# 等待用户按键,参数0表示无限等待
cv2.waitKey(0)  

# 销毁所有OpenCV窗口
cv2.destroyAllWindows()

矩阵表示,如图2.6所示:

2.6-矩阵表示

选取前两个矩阵,把各列相加,接着比较不重复的颜色值的和,如图2.7所示:

2.7-比较不重复的颜色值的和

3.色彩空间转换

代码:

import cv2

def callback(value):
    pass

cv2.namedWindow('color', cv2.WINDOW_NORMAL)
cv2.resizeWindow('mouse', 640, 480)

img = cv2.imread('./dog.jpg')

# 颜色空间转换表以COLOR开头
# 读取照片,默认读入照片为BGR
# 常见的颜色空间转换 2=to
colorspaces = [
    cv2.COLOR_BGR2RGBA, cv2.COLOR_BGR2BGRA,
    cv2.COLOR_BGR2GRAY, cv2.COLOR_BGR2HSV,
    cv2.COLOR_BGR2YUV
]

# 设置拖动条,4个格子
cv2.createTrackbar('curcolor', 'color', 0, 4, callback)

while True:
    # 获取拖动条的值
    index = cv2.getTrackbarPos('curcolor', 'color')

    # 颜色空间转换API:
    cvt_img = cv2.cvtColor(img, colorspaces[index])

    cv2.imshow('color', cvt_img)
    key = cv2.waitKey(10)
    if key & 0xFF == ord('q'):
        break

cv2.destroyAllWindows()

BGR,如图2.8所示:

2.8-BGR

RGB,如图2.9所示:

2.9-RGB

GRAY,如图2.10所示:

2.10-GRAY

HSV,如图2.11所示:

2.11-HSV

YUV,如图2.12所示:

2.12-YUV

(二)OpenCV的重要数据结构--Mat

1.Mat介绍

(1)不同语言中的mat:

opencv用mat这种数据结构来表示图片的

C++是用mat来保存图片的,python中把mat转化成了numpy的ndarray

mat的结构,如图2.13所示:

2.13-mat的结构

C++定义,如图2.14所示:

2.14-mat的结构的C++定义

Mat属性,如图2.15所示:

2.15Mat属性

(2)ndarray的四种常见属性:

属性:

  1. img.data;
  2. img.size;
  3. img.dtype;
  4. img.shape;
  5. img.ndim;

示例(1)如图2.16所示:

2.16-Mat属性示例(1)

示例(2)如图2.17所示:

2.17-Mat属性示例(2)

2. Mat拷贝

(1)Mat共享数据

Mat共享数据,如图2.18所示:

2.18-Mat共享数据

(2)Mat的拷贝

使用numpy提供的深浅拷贝方法即可实现Mat的拷贝:

import cv2
import numpy as np

img = cv2.imread('./dog.jpg')

# 浅拷贝
img2 = img.view()

# 深拷贝
img3 = img.copy()

img[10:100, 10:100] = [0, 0, 255]

cv2.imshow('img', img)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)

cv2.waitKey(0)
cv2.destroyAllWindows()

或者,利用hstack函数堆叠起来对比:

import cv2
import numpy as np

img = cv2.imread('./dog.jpg')

# 浅拷贝
img2 = img.view()

# 深拷贝
img3 = img.copy()

img[10:100, 10:100] = [0, 0, 255]

cv2.imshow('img', np.hstack((img, img2, img3)))

cv2.waitKey(0)
cv2.destroyAllWindows()

浅拷贝与深拷贝结果,如图2.19所示:

2.19-浅拷贝与深拷贝结果

3.访问图像(Mat)的属性

OpenCV中的Mat在python中已经转化为ndarray,通过ndarray的属性即可访问Mate图像的属性

import cv2
import numpy as np

# 读取图片
img = cv2.imread('./dog.jpg')

# 浅拷贝
img2 = img.view()

# 深拷贝
img3 = img.copy()

# 修改原图的部分区域
img[10:100, 10:100] = [0, 0, 255]

# 水平堆叠原图、浅拷贝和深拷贝的图像并显示
cv2.imshow('img', np.hstack((img, img2, img3)))

# 等待按键并关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

4.通道的分离与合并

主要方法:

split(mat)分割图像的通道,merge((ch1,ch2,ch3))融合多个通道

(1)将图片b,g分割后融合

import cv2
import numpy as np

# 创建一个全黑的图像,大小为400x640,3通道,数据类型为np.uint8
img = np.zeros((400, 640, 3), np.uint8)

# 将图像分割成蓝、绿、红三个通道
b, g, r = cv2.split(img)

# 修改蓝色和绿色通道的部分区域为255(白色)
b[10:100, 10:100] = 255
g[10:100, 10:100] = 255

# 合并通道
img2 = cv2.merge((b, g, r))

# 显示原图和各个通道以及合并后的图像
cv2.imshow('img', img)
cv2.imshow('b', b)
cv2.imshow('g', g)
cv2.imshow('img2', img2)

# 等待按键并关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

将图片b,g分割后融合结果,如图2.20所示:

2.20-将图片b,g分割后融合结果

(2)将img和img2水平放置在一起对比:

import cv2
import numpy as np

# 创建一个全黑的图像,大小为400x640,3通道,数据类型为np.uint8
img = np.zeros((400, 640, 3), np.uint8)

# 将图像分割成蓝、绿、红三个通道
b, g, r = cv2.split(img)

# 打印蓝色通道(初始全0)
print('b', b)

# 修改蓝色和绿色通道的部分区域为255(白色)
b[10:100, 10:100] = 255
g[10:100, 10:100] = 255

# 合并通道
img2 = cv2.merge((b, g, r))

# 显示原图(蓝色和绿色通道水平堆叠)和修改后的图像(原图和合并后的图像水平堆叠)
cv2.imshow('img', np.hstack((b, g)))
cv2.imshow('img2', np.hstack((img, img2)))

# 等待按键并关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

将img和img2水平放置在一起对比,如图2.21所示:

2.21-将img和img2水平放置在一起对比

(三)绘制图形

利用OpenCV提供的绘制图形API可以绘制直线,矩形,圆,椭圆等图形:

1.直线

line(img,pt1,pt2,color,thickness,lineType,shift)画直线

参数说明

  • 1.img:在哪个图像上画直线
  • 2. pt1,pt2:开始点,结束点指定线的开始和结束位置
  • 3. color:颜色
  • 4. thickness:线宽
  • 5. lineType:线型,线形为-1,4,8,16,默认为8
  • 6. shift:坐标缩放比例

绘制直线示例:

import cv2
import numpy as np

# 创建纯黑的背景图
img = np.zeros((480, 640, 3), np.uint8)

# 绘制两条直线
cv2.line(img, (10, 20), (300, 400), (0, 0, 255), 5, 4)
cv2.line(img, (180, 100), (480, 580), (0, 0, 255), 5, 16)

# 显示图像
cv2.imshow('draw', img)

# 等待按键并关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

绘制直线,如图2.22所示:

2.22-绘制直线

2.矩形

rectangle(img,pt1,pt2,color,thickness,lineType,shift)画矩形

import cv2
import numpy as np

# 创建纯黑的背景图
img = np.zeros((480, 640, 3), np.uint8)

# 绘制矩形
cv2.rectangle(img, (80, 100), (380, 380), (0, 255, 0), 5)

# 显示图像
cv2.imshow('draw', img)

# 等待按键并关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

画矩形,如图2.23所示:

2.23-画矩形

3.圆

circle(img,center,radius,color[,thickness[,lineType[,shift]]])中括号内参数表示可选参数,画圆

import cv2
import numpy as np

# 创建纯黑的背景图
img = np.zeros((480, 640, 3), np.uint8)

# 画圆
cv2.circle(img, (320, 240), 50, (0, 0, 255), 5)

# 显示图像
cv2.imshow('draw', img)

# 等待按键并关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

画圆,如图2.24所示:

2.24-画圆

4.椭圆

ellipse(img,中心点,长宽的一半,角度,从哪个角度出发,从哪个角度结束....),画椭圆

(1)正椭圆

import cv2
import numpy as np

# 创建纯黑的背景图
img = np.zeros((480, 640, 3), np.uint8)

# 画椭圆
cv2.ellipse(img, (320, 240), (100, 50), 0, 0, 360, (0, 0, 255), 5)

# 显示图像
cv2.imshow('draw', img)

# 等待按键并关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

画正椭圆,如图2.25所示:

2.25-画正椭圆

(2)倾斜15度椭圆

cv2.ellipse(img,(320,240),(100,50),15,0,360,[0,0,255],5,16)

画倾斜15度椭圆,如图2.26所示:

2.26-画倾斜15度椭圆

(3)画半椭圆(0-180度)

cv2.ellipse(img,(320,240),(100,50),15,0,180,[0,0,255],5,16)

如图2.27所示:

2.27-画半椭圆(0-180度)

5.多边形

polylines(img,[pts],isClosed,color[thinckness[,lineType[,shift]])画多边形

(1)闭合多边形

import cv2
import numpy as np

# 创建纯黑的背景图
img = np.zeros((400, 600, 3), np.uint8)

# 绘制多边形
# pts多边形的点集, 必须是int32位
pts = np.array([[250, 100], [150, 300], [50, 280]], np.int32)
# 注意pts是三维的(这里实际是二维点集,在OpenCV中polylines要求点集为三维数组,最后一个维度是点坐标,可理解为形状为(1, n, 2) )
# 更准确的写法如下(将点集reshape为(1, -1, 2) ):
pts = pts.reshape((-1, 1, 2))  
# 或者在创建时就写成三维形式,如:pts = np.array([[[250, 100]], [[150, 300]], [[50, 280]]], np.int32) 
# 下面按照正确形式绘制
cv2.polylines(img, [pts], True, (0, 0, 255), 5)

# 另一种常见正确写法,直接定义三维点集
# pts = np.array([[[250, 100], [150, 300], [50, 280]]], np.int32)
# cv2.polylines(img, pts, True, (0, 0, 255), 5)

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

画闭合多边形,如图2.28所示:

2.28-画闭合多边形

(2)不闭合多边形

import cv2
import numpy as np

# 创建纯黑的背景图
img = np.zeros((400, 600, 3), np.uint8)

# 绘制多边形(非闭合)
pts = np.array([[250, 100], [150, 300], [50, 280]], np.int32)
# OpenCV 的 polylines 函数要求点集为特定三维格式,这里调整点集格式
pts = pts.reshape((-1, 1, 2))  
cv2.polylines(img, [pts], False, (0, 0, 255), 5)

# 显示图像
cv2.imshow('draw', img)

# 等待按键并关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

画不闭合多边形,如图2.29所示:

2.29-画不闭合多边形

(3)fillPoly填充多边形

import cv2
import numpy as np

# 创建纯黑的背景图
img = np.zeros((400, 600, 3), np.uint8)

# 定义多边形的顶点坐标
pts = np.array([[250, 100], [150, 300], [50, 280]], np.int32)
# 调整点集格式为OpenCV要求的三维格式 (n, 1, 2)
pts = pts.reshape((-1, 1, 2))  

# 填充多边形
cv2.fillPoly(img, [pts], (0, 0, 255))

# 显示图像
cv2.imshow('draw', img)

# 等待按键并关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

fillPoly填充多边形如图2.30所示:

2.30-fillPoly填充多边形

6.绘制文本

putText(img,text,org,fontScale,color[,thickness[,lineType[,bottomLeftOrigin]]])绘制文本

参数说明:

  1. text 要绘制的文本
  2. org 文本在图片的左下角坐标
  3. fontFace 字体类型即字体
  4. fontScale 字体大小

(1)显示英文

import cv2
import numpy as np

# 创建纯黑的背景图
img = np.zeros((400, 600, 3), np.uint8)

# 绘制文本
cv2.putText(img, 'Hello OpenCV', (50, 100), cv2.FONT_HERSHEY_COMPLEX, 2, (0, 0, 255), 2)

# 显示图像
cv2.imshow('draw', img)

# 等待按键并关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

显示英文,如图2.31所示:

2.31-显示英文

(2)显示中文

导航到C:/Windows/Fonts,复制任何一个字体样式(ttf),粘贴到mven虚拟机的目录下

from PIL import ImageFont, ImageDraw, Image
import numpy as np
import cv2

# 创建纯白图像
img = np.full((500, 500, 3), fill_value=255, dtype=np.uint8)

# 导入字体文件
font = ImageFont.truetype('./方正粗黑宋简体.ttf', 35)

# 创建一个PIL图片
img_pil = Image.fromarray(img)

draw = ImageDraw.Draw(img_pil)

# 利用draw绘制中文
draw.text((100, 150), '你好,我好', font=font, fill=(0, 255, 0, 0))  
# 重新变为ndarray
img = np.array(img_pil)

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

显示中文,如图2.32所示:

2.32-显示中文

(3)图片中显示文字

from PIL import ImageFont, ImageDraw, Image
import numpy as np
import cv2

# 导入字体文件
font = ImageFont.truetype('./方正粗黑宋简体.ttf', 35)
    
# 在图片内显示字符
img = cv2.imread('./dog.jpg')
    
# 创建一个pillow图片
img_pil = Image.fromarray(img)
    
draw = ImageDraw.Draw(img_pil)
    
# 利用draw绘制中文
draw.text((100, 150), '你好,我好', font=font, fill=(0, 255, 0, 0))   
    
# 重新变为ndarray
img = np.array(img_pil)
    
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()  

图片中显示文字,如图2.33所示:

2.33-图片中显示文字

(四)综合练习

练习:监听鼠标运动绘制图形

思路:

  • 按下按键比如1,进入绘制直线模式,需要记录起始位置,即按下鼠标左键的那一瞬间的坐标位置
  • 左键起来的鼠标坐标为终点,然后绘制
  • 按下l,拖动鼠标,绘制直线
  • 按下r,拖动鼠标,绘制矩形
  • 按下c,拖动鼠标,绘制圆,拖动的长度可以作为半径

示例代码:

import cv2
import numpy as np

# 这是一个全局标志,判断需要画什么类型的图
curshape = 0
startpos = (0, 0)

# 创建背景图
img = np.zeros((480, 640, 3), np.uint8)

# 要监听鼠标的行为,所以必须通过鼠标回调函数实现
def mouse_callback(event, x, y, flags, userdata):
    global curshape, startpos
    if event == cv2.EVENT_LBUTTONDOWN:
        # 记录起始位置
        startpos = (x, y)
    elif event == cv2.EVENT_LBUTTONUP:
        # 判断要画什么类型的图
        if curshape == 0:
            # 画直线
            cv2.line(img, startpos, (x, y), (0, 0, 255), 3)
        elif curshape == 1:
            # 画矩形
            cv2.rectangle(img, startpos, (x, y), (0, 0, 255), 3)
        elif curshape == 2:
            # 画圆
            # 计算半径
            a = (x - startpos[0])
            b = (y - startpos[1])
            # 注意,画圆时,半径必须为整数
            r = int((a**2 + b**2) ** 0.5)
            cv2.circle(img, startpos, r, (0, 0, 255), 3)
        else:
            print('暂不支持')

# 创建窗口
cv2.namedWindow('drawshape', cv2.WINDOW_NORMAL)
# 设置鼠标回调函数
cv2.setMouseCallback('drawshape', mouse_callback)

while True:
    cv2.imshow('drawshape', img)
    # 检测按键
    key = cv2.waitKey(1)
    if key == ord('q'):
        break
    elif key == ord('l'):
        curshape = 0
    elif key == ord('r'):
        curshape = 1
    elif key == ord('c'):
        curshape = 2

cv2.destroyAllWindows()

监听鼠标运动绘制图形,如图2.34所示:

2.34-监听鼠标运动绘制图形
Logo

音视频技术社区,一个全球开发者共同探讨、分享、学习音视频技术的平台,加入我们,与全球开发者一起创造更加优秀的音视频产品!

更多推荐