最常见的色彩空间就是RGB,人眼也是基于RGB的色彩空间去分辨颜色的。 OpenCV默认使用的是BGR。BGR和RGB色彩空间的区别在于图片在色彩通道上的排列顺序不同。
显示图片的时候需要注意适配图片的色彩空间和显示环境的色彩空间。比如:传入的图片是BGR色彩空间,显示的环境是RGB色彩空间,就会出现颜色混乱的情况。
OpenCV最常用的色彩空间就是HSV
为什么使用HSV? 方便OpenCV做图像处理,比如根据HUE的值就可以判断背景颜色。
HSL顶部是纯白色的,不管是什么色相。HSB 与 HSL 在字面意思上是一样的:
在原理和表现上,HSL 和 HSB 中的 H 完全一致,但是二者的 S 、L 和 B 不一样:
YUV,是一种颜色编码方法。常使用在各个视频处理组件中。YUV在对照片或视频编码时,考虑到人类的感知能力,允许降低色度的带宽。
YUV的发明是由于彩色电视与黑白电视的过渡时期。 YUV最大的优点在于只需占用极少的带宽。
import cv2
from pandas import DataFrame
dog = cv2.imread('./doge.jpg')
# 用每一个像素创建DataFrame
df = DataFrame(dog.reshape(11,3))
# 计算有多少个不同的颜色
df.shape[0] - df.duplicated().sum()
import cv2
def callback(value):
pass
cv2.namedWindow('color',cv2.WINDOW_NORMAL)
cv2.resizeWindow('mouse', 640, 480)
# 常见的颜色空间
colorspaces = [cv2.COLOR_BGR2RGBA, cv2.COLOR_BGR2BGRA, cv2.COLOR_BGR2GRAY, cv2.COLOR_BGR2HSV, cv2.COLOR_BGR2YUV]
cv2.createTrackbar('curcolor', 'color', 0, 4, callback)
while True:
index = cv2.getTrackbarPos('curcolor', 'color')
# 颜色空间转换
cvt_img = cv2.cvtColor(img, colorspaces[index])
cv2.imshow('color', cvt_img)
key = cv2.waitKey(10)
if key & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
Mat 是 OpenCV 在 C++ 语言中用来表示图像数据的一种数据结构,在 Python 中转化为 numpy 的 ndarray
import cv2
# OpenCV用mat这种数据结构来表示图片
# C++中用mat来保存图片,python中把mat转化成了numpy的ndarray
cv2.imshow
# numpy.ndarray
type(img)
#render175099124 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#render175099124 .error-icon{fill:#552222;}#render175099124 .error-text{fill:#552222;stroke:#552222;}#render175099124 .edge-thickness-normal{stroke-width:2px;}#render175099124 .edge-thickness-thick{stroke-width:3.5px;}#render175099124 .edge-pattern-solid{stroke-dasharray:0;}#render175099124 .edge-pattern-dashed{stroke-dasharray:3;}#render175099124 .edge-pattern-dotted{stroke-dasharray:2;}#render175099124 .marker{fill:#333333;stroke:#333333;}#render175099124 .marker.cross{stroke:#333333;}#render175099124 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#render175099124 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#render175099124 .cluster-label text{fill:#333;}#render175099124 .cluster-label span{color:#333;}#render175099124 .label text,#render175099124 span{fill:#333;color:#333;}#render175099124 .node rect,#render175099124 .node circle,#render175099124 .node ellipse,#render175099124 .node polygon,#render175099124 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#render175099124 .node .label{text-align:center;}#render175099124 .node.clickable{cursor:pointer;}#render175099124 .arrowheadPath{fill:#333333;}#render175099124 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#render175099124 .flowchart-link{stroke:#333333;fill:none;}#render175099124 .edgeLabel{background-color:#e8e8e8;text-align:center;}#render175099124 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#render175099124 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#render175099124 .cluster text{fill:#333;}#render175099124 .cluster span{color:#333;}#render175099124 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#render175099124 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}HeaderData
class CV_EXPORTS Mat
{
public:
...
int dims; //维数
int rows, cols; //行列数
uchar *data; //存储数据的指针
int *refcount; //引用计数
...
};
字段 | 说明 | 字段 | 说明 |
---|---|---|---|
dims | 维度 | channels | 通道数 RGB是3 |
rows | 行数 | size | 矩阵大小 |
cols | 列数 | type | dep+dt+chs CV_8UC3 |
depth | 像素的位深 | data | 存放数据 |
ndarray中常见属性:
#render2010523814 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#render2010523814 .error-icon{fill:#552222;}#render2010523814 .error-text{fill:#552222;stroke:#552222;}#render2010523814 .edge-thickness-normal{stroke-width:2px;}#render2010523814 .edge-thickness-thick{stroke-width:3.5px;}#render2010523814 .edge-pattern-solid{stroke-dasharray:0;}#render2010523814 .edge-pattern-dashed{stroke-dasharray:3;}#render2010523814 .edge-pattern-dotted{stroke-dasharray:2;}#render2010523814 .marker{fill:#333333;stroke:#333333;}#render2010523814 .marker.cross{stroke:#333333;}#render2010523814 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#render2010523814 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#render2010523814 .cluster-label text{fill:#333;}#render2010523814 .cluster-label span{color:#333;}#render2010523814 .label text,#render2010523814 span{fill:#333;color:#333;}#render2010523814 .node rect,#render2010523814 .node circle,#render2010523814 .node ellipse,#render2010523814 .node polygon,#render2010523814 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#render2010523814 .node .label{text-align:center;}#render2010523814 .node.clickable{cursor:pointer;}#render2010523814 .arrowheadPath{fill:#333333;}#render2010523814 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#render2010523814 .flowchart-link{stroke:#333333;fill:none;}#render2010523814 .edgeLabel{background-color:#e8e8e8;text-align:center;}#render2010523814 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#render2010523814 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#render2010523814 .cluster text{fill:#333;}#render2010523814 .cluster span{color:#333;}#render2010523814 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#render2010523814 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}Mat AMat BHeaderDataHeader
在Python中Mat数据对应numpy的ndarray,使用numpy提供的深浅拷贝方法即可实现Mat的拷贝。
import cv2
import numpy as np
img = cv2.imread('./doge.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()
OpenCV中的Mat在Python中已经转化为ndarray,通过ndarray的属性可访问Mat图像的属性:
import cv2
import numpy as np
img = cv2.imread('doge.jpg')
# shape属性中包括了3个信息
# 高度、长度、通道数
print(img.shape)
# (54, 60, 3)
# 图像占用多大空间
# 高度 * 长度 * 通道数
print(img.size)
# 9720
# 图像中每个元素的位深
print(img.dtype)
# uint8
# 图像的分割与融合
import cv2
import numpy as np
img = np.zeros((480, 640, 3), np.uint8)
# 分割通道
b, g, r = cv2.split(img)
b[10:100, 10:100] = 255
g[10:100, 10:100] = 255
img2 = cv2.merge((b, g, r))
# 一维
cv2.imshow('img_1', np.hstack((b, g)))
# 三维
cv2.imshow('img_2', np.hstack((img, img2)))
cv2.waitKey(0)
cv2.destroyAllWindows()
img_1:
img_2:
利用OpenCV一共的绘制图形API可以轻松在图像上绘制各种图形,例如:直线、矩形、圆、椭圆等。
# 绘制直线
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, 1)
cv2.line(img, (340,400), (630,20), (0, 0, 255), 5, 16, 1)
cv2.imshow('draw', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 绘制矩形
import cv2
import numpy as np
img = np.zeros((480, 640, 3), np.uint8)
cv2.rectangle(img, (80,100), (380,400), (0, 255, 0), 5, 8)
cv2.imshow('draw', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 绘制圆
import cv2
import numpy as np
img = np.zeros((480, 640, 3), np.uint8)
cv2.circle(img, (320,240), 50, (0, 255, 0))
cv2.circle(img, (400,300), 45, (255, 0, 0), 5, 16)
cv2.imshow('draw', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 绘制椭圆
import cv2
import numpy as np
img = np.zeros((480, 640, 3), np.uint8)
# 完整椭圆
cv2.ellipse(img, (320,120), (120, 20), 0, 0, 360, (0, 0, 255), 2, 16)
# 椭圆弧段
cv2.ellipse(img, (320,240), (100, 50), 0, 0, 240, (0, 255, 0), 2, 16)
# 斜椭圆
cv2.ellipse(img, (320,360), (80, 30), 30, 0, 360, (255, 0, 0), 2, 16)
cv2.imshow('draw', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 绘制多边形
import cv2
import numpy as np
img = np.zeros((480, 640, 3), np.uint8)
# 单点集闭合多边形
pts = np.array([(250,100), (150,280), (280,320), (320,120)], np.int32)
cv2.polylines(img, [pts], True, (255, 255, 255), 2, 16)
# 单点集填充多边形
pts = np.array([(400,100), (300,280), (430,320), (470,120)], np.int32)
cv2.fillPoly(img, [pts], (255, 0, 0))
cv2.imshow('draw', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
注意:点集的集合可以放多个点集,则需要使用中括号括起来以表示点集的集合,多点集 [pts pts]
# 绘制文本
import cv2
import numpy as np
img = np.zeros((480, 640, 3), np.uint8)
cv2.putText(img, 'Hello OpenCV', (50,50), cv2.FONT_HERSHEY_COMPLEX, 2, (255, 255, 255))
cv2.imshow('draw', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 绘制中文
# OpenCV没有办法直接绘制中文
# 使用Pillow包
from PIL import ImageFont, ImageDraw, Image
import cv2
import numpy as np
# 创建纯白背景
img = np.full((200, 200, 3), fill_value=255, dtype=np.uint8)
# 导入字体文件
font = ImageFont.truetype('./PingFang.ttc', 15)
# 创建Pillow图片
img_pil = Image.fromarray(img)
draw = ImageDraw.Draw(img_pil)
# 利用draw去绘制中文
draw.text((100, 100), '中午好', font=font, fill=(0, 255, 0, 0))
# 重新变回ndarray
img = np.array(img_pil)
cv2.imshow('pillow', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
例题:使用OpenCV实现Python程序,程序具有以下功能:
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
# 引入非本层的局部变量使用关键字:nonlocal
if event == cv2.EVENT_LBUTTONDOWN:
# 记录起始位置
startpos = (x,y)
elif event == cv2.EVENT_LBUTTONUP:
# 判断要画什么类型的图
if curshape == 0: # 画直线
cv2.line(img, startpos, (x,y), (255,255,255), 3, 16)
elif curshape == 1: # 画矩形
cv2.rectangle(img, startpos, (x,y), (255,255,255), 3, 16)
elif curshape == 2: # 画圆
# 计算半径 转换为整数
r = int(((x - startpos[0]) ** 2 + (y - startpos[1]) ** 2) ** 0.5)
cv2.circle(img, startpos, r, (255,255,255), 2)
else: # 其他按键
print("暂不支持绘制其他图形")
# 创建窗口
cv2.namedWindow('draw_shape', cv2.WINDOW_NORMAL)
# 设置鼠标回调函数
cv2.setMouseCallback('draw_shape', mouse_callback)
while True:
cv2.imshow('draw_shape', img)
# 检测案件
key = cv2.waitKey(0)
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()