(七) 色彩空间
什么是色彩空间,人们建立了多种色彩模型,以一维、二维、三维甚至四维空间坐标来表示某一色彩,这种坐标系统所能定义的色彩范围即色彩空间
色彩空间有很多,但是常用的色彩空间一共5种:RGB、HSV、HSI、YCrCb、YUV,简单讲一下这5个色彩空间。
RGB就不用多说了,RGB是我门经常用到的;
HSV也称六角锥体模型,是根据颜色的直观特性创建的一种颜色空间,这个颜色空间是本节课讲解的一个重点。
HSI是从人的视觉系统出发,用色调( Hue )、色饱和 度( Saturation 或 Chroma )和亮度( Intensity 或 Brightness )来描述颜色。 HSI 颜色空间可以用一个圆 锥空间模型来描述
YCrCb主要用于优化彩色视频信号的传输,使其向后相容老式黑白电视,这个可以用来检测皮肤和检测人脸
YUV是被欧洲电视系统所采用的一种颜色编码方法(属于PAL),是PAL和SECAM模拟彩色电视制式采用的颜色空间。
色彩空间的转换
OpenCV提供多种将图像的色彩空间转换为另一个色彩空间的方法,转换方法的方法名一般为 “原色彩空间2需要转化的色彩空间”,下面我们以图像的RGB色彩转换为其他四种色彩空间和GRAY色彩空间。
# -*- coding: UTF8 -*-
import cv2 as cv
import numpy as np
def ColorSpace(image):
"""
色彩空间转化
RGB转换为其他色彩空间
"""
gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
cv.imshow("gray",gray)
hsv=cv.cvtColor(image,cv.COLOR_RGB2HSV)
cv.imshow("hsv",hsv)
yuv=cv.cvtColor(image,cv.COLOR_RGB2YUV)
cv.imshow("yuv",yuv)
ycrcb=cv.cvtColor(image,cv.COLOR_RGB2YCrCb)
cv.imshow("ycrcb",ycrcb)
# 读入图片文件
src = cv.imread('images//test.jpg')
ColorSpace(src)
# 等待用户操作
cv.waitKey(0)
# 释放所有窗口
cv.destroyAllWindows()
image.png
标记图像中的特定颜色
一般对颜色空间的图像进行有效处理都是在HSV空间进行的,然后对于基本色中对应的HSV分量需要给定一个严格的范围:
H: 0 — 180
S: 0 — 255
V: 0 — 255
以下是不同颜色的HSV最大最小的范围:
HSV最大最小的范围
以下代码是标注出图像中的黑色部分,黑色部分将以白色显示,其他颜色部分将以黑色显示,颜色标注OpenCV 提供了一个方法,inRange()。该方法提供三个参数,
第一个参数是图像色彩空间即hsv值,
第二个参数是hsv的最小查找范围,
第三个参数是hsv的最大查找范围。代码运行后,将会标注出图像的黑色部分。
# -*- coding: UTF8 -*-
import cv2 as cv
import numpy as np
capture = cv.VideoCapture("video/test.mp4")
while (True):
ret, frame = capture.read()
if ret == False:
break;
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
lower_hsv = np.array([0, 0, 0])
upperb_hsv = np.array([180, 255, 46])
# 第一个参数是图像色彩空间即hsv值,
# 第二个参数是hsv的最小查找范围,
# 第三个参数是hsv的最大查找范围。代码运行后,将会标注出图像的黑色部分。
mask = cv.inRange(hsv, lowerb=lower_hsv, upperb=upperb_hsv)
cv.imshow("video_mask", mask)
cv.imshow("video", frame)
c = cv.waitKey(40)
if c == 27:
break;
颜色标注
(八)图像运算
图像运算也就是像素运算,简单的说就是利用算术运算或逻辑运算,对图像的每个像素进行处理(例如两个图像的合并)。
注意:我们在处理两个图像时,图像的像素大小和类型要完全一致,否则OpenCV就会报错。
算术运算
# -*- coding: UTF8 -*-
import cv2 as cv
import numpy as np
def add(image1, image2):
"""图片相加"""
dst = cv.add(image1, image2)
cv.imshow("add image", dst)
def subtract(image1, image2):
"""图片相减"""
dst = cv.subtract(image1, image2)
cv.imshow("subtract image", dst)
def divide(image1, image2):
"""图片相除"""
dst = cv.divide(image1, image2)
cv.imshow("divide image", dst)
def multiply(image1, image2):
"""图片相乘"""
dst = cv.multiply(image1, image2)
cv.imshow("multiply image", dst)
image1=cv.imread('images//chang1.jpg')
image2=cv.imread('images//chang2.jpg')
add(image1,image2)
# 等待用户操作
cv.waitKey(0)
# 释放所有窗口
cv.destroyAllWindows()
原图1
原图2
图片相加
图片相减
图片相乘
2.逻辑运算
def logic(image1,image2):
"""逻辑运算"""
# 与操作
dst=cv.bitwise_and(image1,image2)
cv.imshow("logic",dst)
# 或操作(与相加操作类似)
dst = cv.bitwise_or(image1, image2)
cv.imshow("logic", dst)
# 非操作(像素取反)
dst = cv.bitwise_not(image1)
cv.imshow("logic", dst)
image1=cv.imread('images//chang1.jpg')
image2=cv.imread('images//chang2.jpg')
logic(image1,image2)
# 等待用户操作
cv.waitKey(0)
# 释放所有窗口
cv.destroyAllWindows()
与或非操作
其他算数运算
def others(image1,image2):
#计算每个通道的平均值
m1= cv.mean(image1)
m2 = cv.mean(image2)
#计算每个通道的平均值和方差
m1,dev1=cv.meanStdDev(image1)
m2,dev2=cv.meanStdDev(image2)
print(m1,dev1)
print(m2,dev2)
image1=cv.imread('images//chang1.jpg')
image2=cv.imread('images//chang2.jpg')
others(image1,image2)
# 等待用户操作
cv.waitKey(0)
# 释放所有窗口
cv.destroyAllWindows()
平均值和方差
修改亮度和对比度:
def contrast_brightness(image,c,b):
"""
修改亮度和对比度
c:对比度
b:亮度
"""
#获取图片的高、宽和通道数
h,w,ch=image.shape
#创建一个全黑色的图片
blank=np.zeros([h,w,ch],image.dtype)
#调整亮度和对比度
dst=cv.addWeighted(image,c,blank,1-c,b)
cv.imshow("con-bri", dst)
image2=cv.imread('images//chang2.jpg')
contrast_brightness(image2,5,5)
# 等待用户操作
cv.waitKey(0)
# 释放所有窗口
cv.destroyAllWindows()
(九)ROI
简单的说就是对图像感兴趣的区域,机器视觉、图像处理中,从被处理的图像以方框、圆、椭圆、不规则多边形等方式勾勒出需要处理的区域,称为感兴趣区域,ROI。举个例子来说:有一副图片,图片上有各种动物i,但是你只喜欢图片里的狗,那么这个狗所在的区域就是感兴趣的区域(ROI)。
# -*- coding: UTF8 -*-
import cv2 as cv
import numpy as np
src= cv.imread('images/test.jpg')
cv.namedWindow('input image', cv.WINDOW_AUTOSIZE)
cv.imshow('input image', src)
# 高度从42像素开始到282像素
# 宽度从184像素开始到355像素
# 高度起始位置是从图片的顶部算起,宽度起始位置是从图片的左侧算起
# 本例中的起始位置和结束位置是通过PhotoShop 测量出来的,在实际应用中这两个位置是通过算法计算出来的
face = src[85:168, 420:486]
# 效果见图1,我们取出了原图的人脸
cv.imshow("取出的图像".encode("gbk").decode(errors="ignore"), face)
# 将取出的区域改变为灰度图像
gray = cv.cvtColor(face, cv.COLOR_BGR2GRAY)
# 将灰度图像变为RGB图像
# 这里改变色彩空间的原因是灰度图像是单通道的,原图是三通道的,无法合并
# 所以需要先转换为三通道的RGB色彩空间
backface = cv.cvtColor(gray, cv.COLOR_GRAY2BGR)
# 将取出并处理完的图像和原图合并起来
src[85:168, 420:486] = backface
# 效果见图2
cv.imshow("合并后的图像".encode("gbk").decode(errors="ignore"), src)
cv.waitKey(0)
cv.destroyAllWindows()
image.png
(十)洪填充
泛洪填充算法又称洪水填充算法是在很多图形绘制软件中常用的填充算法,最熟悉不过就是windows paint的油漆桶功能。算法的原理很简单,就是从一个点开始附近像素点,填充成新的颜色,直到封闭区域内的所有像素点都被填充新颜色为止。泛红填充实现最常见有四邻域像素填充法,八邻域像素填充法,基于扫描线的像素填充方法。根据实现又可以分为递归与非递归(基于栈)。
import cv2 as cv
import numpy as np
def fill_color(image):
"""
漫水填充:会改变图像
"""
# 复制图片
copyImg = image.copy()
# 获取图片的高和宽
h, w = image.shape[:2]
# 创建一个h+2,w+2的遮罩层,
# 这里需要注意,OpenCV的默认规定,
# 遮罩层的shape必须是h+2,w+2并且必须是单通道8位,具体原因我也不是很清楚。
mask = np.zeros([h + 2, w + 2], np.uint8)
# 这里执行漫水填充,参数代表:
# copyImg:要填充的图片
# mask:遮罩层
# (30,30):开始填充的位置(开始的种子点)
# (0,255,255):填充的值,这里填充成黄色
# (100,100,100):开始的种子点与整个图像的像素值的最大的负差值
# (50,50,50):开始的种子点与整个图像的像素值的最大的正差值
# cv.FLOODFILL_FIXED_RANGE:处理图像的方法,一般处理彩色图象用这个方法
cv.floodFill(copyImg, mask, (30, 30), (0, 255, 255), (100, 100, 100), (50, 50, 50), cv.FLOODFILL_FIXED_RANGE)
cv.imshow("fill color", copyImg)
def fill_binary():
"""
二值填充:不改变图像,只填充遮罩层本身,忽略新的颜色值参数
"""
# 创建一个400*400的3通道unit8图片
image = np.zeros([400, 400, 3], np.uint8)
# 将图片的中间区域变为白色
image[100:300, 100:300, :] = 255
cv.imshow("fill color", image)
mask = np.ones([402, 402, 1], np.uint8)
# 将遮罩层变为黑色
mask[101:301, 101:301] = 0
# 在图像的中间填充,颜色为红色,用FLOODFILL_MASK_ONLY方法填充
cv.floodFill(image, mask, (200, 200), (0, 0, 255), cv.FLOODFILL_MASK_ONLY)
cv.imshow("filled", image)
src= cv.imread('images/test.jpg')
fill_binary()
cv.waitKey(0)
cv.destroyAllWindows()
漫水填充
二值填充