第二部分、图像操作
- 第一节、图像读取与显示
- 1.图像理解
- 2.图像读取与显示
- 3.代码练习与测试
- 第二节、图像色彩空间转换
- 1.图像色彩空间
- 2.函数说明与解释
- 3.代码练习与测试
- 第三节、图像对象的创建与赋值
- 1.图像对象属性
- 2.图像对象创建与赋值
- 3.代码练习与测试
- 第四节、图像像素的读写操作
- 1.图像像素
- (1)**像素与分辨率**
- (2)OpenCV中的像素
- 2.像素遍历
- (1)像素遍历
- (2)像素读写
- 3.代码练习与测试
- 第五节、图像算术操作
- 1.算术操作
- (1)加、减、乘、除
- 2.像素算术操作
- (1)OpenCV函数实现算术操作
- (2)mask参数
- 3.代码练习于测试
- 学习参考
第一节、图像读取与显示
1.图像理解
人眼中的图像–假设是一个灰度/彩色图片,而在计算机眼中都是像素点的数字。
灰度图像 – 单通道
彩色图像– 三通道
2.图像读取与显示
读取:2个Api函数–imread、imshow
# 导入OpenCV支持的包,这个cv2并不是opencv的版本号,只是这个包的名字
import cv2 as cv
# 导入Numpy支持的包,因为像素点对于计算机来说都是数组存储
import numpy as np
# 读取图像
imread()
# 显示图像
imshow()
#加载图像的顺序都是B-G-R
函数与参数:
cv.imread(filename[,flags])->retval(Numpy数组)
# filename表示文件路径
# []内的参数表示可选,也可以不填
# retval是读取之后返回的结果
cv.imshow(winname,mat)->None
# winname表示窗口标题(只能是英文的)
# mat表示图像对象,也就是上面返回的retval
显示与等待时间:
cv.waitKey(0)
# 表示一直等待,直到任意一个键盘操作
cv.waitKey(1000)
# 表示等待1000ms或者1s
# 如果不加等待结果就是一闪而过
3.代码练习与测试
代码示例:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
def show_image():
image = cv.imread("F:\python\研究生python\Datawhale\计算机视觉OpenCV\github_opencv\\lena.jpg") # BGR的读取顺序
cv.imshow("lena",image)
cv.waitKey(1000)
cv.destroyAllWindows() # 销毁该程序创建的所有窗口
windows下读取文件路径的选择:
第二节、图像色彩空间转换
1.图像色彩空间
在OpenCV中我们最常用的色彩空间是HSV
,RGB
,YCrCb
三种。
HSV和YCrCb空间对于亮色的区别比RGB好,所以在使用的时候需要灵活选择使用那种色彩空间。
- RGB色彩空间,设备独立
- HSV色彩空间,对计算机友好,区分各种色彩
- YCrCb, Y分量表示信息,CrCb可以被压缩
- RGB是计算机显示器的标准支持色彩系统
- RGB的取值范围0~255
- HSV取值范围H:0180,SV:0255
2.函数说明与解释
图像色彩转换都有下面的三个情况。
- 从一个色彩空间转换到另外一个色彩空间
- 信息传递与损失
- 过程可逆与不可逆
实现色彩转换可以通过使用函数实现:
cv.cvtColor(src,code[,dst[,dstCn]])->dst # []表示的是可选的参数,也可以不写
# src表示输入图像,类型为CV_8U(字符类型)、CV_32F(浮点类型)
# code表示:从什么转换到什么,下面是cv的枚举类型对应的含义。
# cv::COLOR——BGR2RGB = 4 通道交换
# cv::COLOR——BGR2GRAY = 6 彩色到灰度
# cv::COLOR——GRAY2BGR = 8 灰度到彩色
# cv::COLOR——BGR2HSV = 40 彩色到彩色,BGR转换成HSV
# 注意从彩色到灰色,再从灰色到彩色,只是通道数恢复了,其中的信息损失是无法恢复的。
3.代码练习与测试
def color_space_demo():
image = cv.imread(r"F:\python\opencv-4.x\samples\data\lena.jpg")
cv.imshow("lena",image)
hsv = cv.cvtColor(image,cv.COLOR_BGR2HSV)
ycrcb = cv.cvtColor(image,cv.COLOR_BGR2YCrCb)
cv.imshow("hsv",hsv)
cv.imshow("ycrcb",ycrcb)
cv.waitKey(0)
cv.destroyAllWindows()
转换的示例:
def color_space_demo():
image = cv.imread(r"F:\python\opencv-4.x\samples\data\lena.jpg")
cv.imshow("lena", image)
gray = cv.cvtColor(image, 6) # 相当于cv.COLOR_BGR2GRAY
image2 = cv.cvtColor(gray, 8) # 相当于cv.COLOR_GRAY2BGR
cv.imshow("gray", gray)
cv.imshow("image2", image2)
print("image's shape:", image.shape)
print("gray's shape:", gray.shape)
print("image2's shape:", image2.shape)
cv.waitKey(0)
cv.destroyAllWindows()
如果我们把彩色图片转换成灰度再转换回来,可以发现他丢失了色彩的信息,如果打印通道数的话发现虽然通道数转换回来了,但是里面的信息已经丢失了,也就是转换之后每个通道中的值是相同的,并不是原来彩色图片中通道的值。
但是BGR和HSV,YCrCb这些彩色之间是可以相互转换的。
第三节、图像对象的创建与赋值
1.图像对象属性
- 图像宽高 image.shape (h, w, c) – (高,宽,通道数)
- 图像深度 image
如下图所示的一个png图片(不包含透明通道,一共3个通道)位深度表示每个通道占的1个字节(8bit),所以下图的一个png图片所有通道深度一共是24。如果使用folat32存储图片的话,一个png图片的深度就是3 * 32 = 96。 - 图像数据类型 image.dtype
- 图像通道 image.shape
- 如何加载不同通道的图像
用c/c++做opencv中的一些数据类型和opencv-python的一些数据类型。
左边是c/c++做opencv,其中U对应unit,S对应int,F对应float,C对应通道数。
右边是用python做opencv。
一般第一步都是将图像转成float32类型,以免后续计算的时候有精度丢失出现,最后再转成int类型得到图片。
2.图像对象创建与赋值
在OpenCV中所有图片对象都是Numpy Array
。
创建图像就是创建numpy array
# Numpy常用函数
1. numpy.array
2. numpy.zeros
3. numpy.zeros_like # 生成一个和读入图片大小完全一致,但是黑色的图片,即在所有位置都填成0
4. numpy.asarray # 把正常的python数组变成Numpy数组
5. numpy.copy
6. numpy.reshape
一些函数的解释:
- numpy.array(object, dtype=None, *, copy=True, order=‘K’, subok=False, ndmin=0, like=None)
- object 数组
- dtype 数据类型
- numpy.zeros(shape, dtype=float, order=‘C’, *, like=None)
- 数组维度
- dtype 数据类型
- numpy.asarray(a, dtype=None, order=None, *, like=None)
- 数组对象
- dtype 数据类型
- numpy.reshape(a, newshape, order=‘C’)
- 数组维度
- dtype 数据类型
创建图片一般不会像左图所示自己去写,而是使用右图中的函数去创建。
def make_numpy():
m = np.zeros((3, 3, 3), dtype=np.uint8)
print(m)
m[:] = 255
print(m)
m[:] = (255, 0, 0)
print(m)
3.代码练习与测试
自己创建一些numpy array并且输出图片
def make_numpy_show():
m = np.zeros((512, 512, 3), dtype=np.uint8)
m.shape # 分别是H W C
m[:] = 255
cv.imshow("m1", m)
n = np.zeros_like(m) # 用m的大小去创造一个n,并且n中元素全为0
print(n)
cv.imshow("n", n)
n[:256] = (255, 0, 0)
cv.imshow("n2", n)
cv.waitKey(0)
cv.destroyAllWindows()
输出示例如下所示:
尝试给画布左右分隔添加颜色
def try_color():
m = np.zeros((512, 512, 3), dtype=np.uint8)
print(m.shape) # 分别是H W C
m[:256] = (255, 0, 0)
m[0:256, 0:256] = (0, 0, 255)
print(m)
cv.imshow("m1", m)
cv.waitKey(0)
cv.destroyAllWindows()
第四节、图像像素的读写操作
1.图像像素
(1)像素与分辨率
如下图高分辨率就是8x8的像素,低分辨率就是2x2的像素。(高分辨率携带的像素数量多于低分辨率)
例如,我们有一个大熊猫的图片,高分辨率就可以连大熊猫的毛发都能看到,低分辨率可能画出来大熊猫的轮廓都很吃力。
- 像素实际大小:dpi x inches = 像素总数
- 术语dpi:每英寸的点数目,96dpi — 针对打印
- 术语ppi:
每英寸的像素数目
— 针对图像分辨率
下图所示的就是相机像素从一百万到四百万对应的像素数目。
(2)OpenCV中的像素
- 灰度图像排序
- 灰度图片只有一个通道
- 左上角的值就是图片中的左上角第一个像素点
- 彩色图片排序
- 彩色图片有三个通道
- 开始的像素点在左上角,结束的像素点在右下角
2.像素遍历
(1)像素遍历
像素遍历参照上面灰度图片和彩色图片的示意图,使用for循环来遍历numpy数组,一个for循环管高度方向的,另一个for循环管宽度方向,从左到右,从上到下遍历数组。
- 像素遍历本质就是numpy数组
- 访问假设变量image
- 获取图像维度信息: image.shape – 彩色图像分别是H(高) W(宽) C(通道数),灰度图片就是H(高) W(宽)
- 图像访问像素: image[row, col]
- 图像赋值像素: image[row, col] = (b,g,r) – 彩色图片每个位置对应三个值
(2)像素读写
- 读写像素,彩色图像:
- b, g, r = image[row, col]
- image[row, col] = (255-b, 255-g, 255-r)
- 读写像素,灰度图像:
- pv = image[row, col]
- image[row, col] = 255-pv
3.代码练习与测试
def visit_pixel_demo():
image = cv.imread(r"F:\python\opencv-4.x\samples\data\lena.jpg")
cv.imshow("lena",image)
h, w, c = image.shape
print("h: ", h, "w: ", w, "c:", c)
for row in range(h):
for col in range(w):
b, g, r = image[row, col]
image[row, col] = (255-b, 255-g, 255-r) # 取反色,把原图RGB,通过255-,变成反色。
cv.imshow("visited", image)
cv.waitKey(0)
cv.destroyAllWindows()
结果示例:
同理我们可以如上节所学代码实现在图片的某一个部分添加颜色快。
def visit_pixel_demo2():
image = cv.imread(r"F:\python\opencv-4.x\samples\data\lena.jpg")
cv.imshow("lena", image)
h, w, c = image.shape
print("h: ", h, "w: ", w, "c:", c)
print(image.dtype)
image[0:256, 256:512] = (0, 0, 255) # 在右上角添加了一个红色块
cv.imshow("visited", image)
cv.waitKey(0)
cv.destroyAllWindows()
第五节、图像算术操作
1.算术操作
(1)加、减、乘、除
- 加法
img1[row, col] + img2[row, col] --> dst[row, col] # img1和img2的大小要相同,对应像素点相加结果给dst
黑色是(0, 0, 0)所以和什么像素值相加就是什么什么,而中间OpenCV绿色加粉色最后就是米黄色的OpenCV。
- 减法
img2[row, col] - img1[row, col] --> dst[row, col] # img1和img2的大小要相同,对应像素点相减结果给dst
- 乘法
img1[row, col] * img2[row, col] --> dst[row, col] # img1和img2的大小要相同,对应像素点相乘结果给dst
- 除法
img2[row, col] / img1[row, col] --> dst[row, col] # img1和img2的大小要相同,对应像素点相除结果给dst
2.像素算术操作
(1)OpenCV函数实现算术操作
但是在OpenCV中并不需要我们手动写计算式,而是调用现有的函数就可以。
1.加法 cv.add(src1, src2[, dst[, mask[, dtype]]]) ->dst
2.减法 cv.subtract(src1,src2[,dst[,mask[,dtype]]])->dst
3.乘法 cv.multiply(src1,src2[,dst[,scale[,dtype]]])->dst
4.除法 cv.divide(src1, src2[, dst[, scale[, dtype]]])->dst
参数说明:
- src1 & src2都是图像
- 加法,保证不越界:saturate(src1 + src2) --> 0~255 - 意思是两个数(0-255)做加法(0-512)如果大于255,就赋值成255;如果做减法小于0,就赋值成0。
- 加法和减法是有mask的,乘法除法没有。
(2)mask参数
因为mask参数只在加法和减法中出现,所以以加法为例。
cv.add(src1, src2[, dst[, mask[, dtype]]]) --> dst
- src1输入图像1
- src2输入图像2
- mask表示模板,只包括0和非零部分。
结果如下图所示:做加法的时候只将mask为1的部分做加法,其余部分和mask一样补0。
mask不同,提取的图像区域不同,所以当需要提取不规则区域的时候可以使用mask
3.代码练习于测试
# 算术操作实现加、减、乘、除
def arithmetic_demo():
image1 = cv.imread(r"F:\python\opencv-4.x\samples\data\opencv-logo.png")
image2 = np.zeros_like(image1)
image2[:, :] = (110, 0, 250)
image1 = cv.resize(image1, None, fx=0.5, fy=0.5) # 调整图片大小
image2 = cv.resize(image2, None, fx=0.5, fy=0.5) # 调整图片大小
cv.imshow("img1", image1)
cv.imshow("img2", image2)
added = cv.add(image1, image2) # 加法
subbed = cv.subtract(image1, image2) # 减法
multiplied = cv.multiply(image1, image2) # 乘法
divided = cv.divide(image1, image2) # 除法
cv.imshow("added", added)
cv.imshow("subbed", subbed)
cv.imshow("multiplied", multiplied)
cv.imshow("divided", divided)
cv.waitKey(0)
cv.destroyAllWindows()
结果示例:
使用mask之后的效果:
# 使用mask实现的算术(加法、减法)操作实现加法
def arithmetic_demo_mask():
image1 = cv.imread(r"F:\python\opencv-4.x\samples\data\opencv-logo.png")
image2 = np.zeros_like(image1)
image2[:, :] = (110, 0, 250)
image1 = cv.resize(image1, None, fx=0.5, fy=0.5) # 调整图片大小
image2 = cv.resize(image2, None, fx=0.5, fy=0.5) # 调整图片大小
cv.imshow("img1", image1)
cv.imshow("img2", image2)
h, w, c = image1.shape
print("h: ", h, "w: ", w, "c:", c)
mask = np.zeros((h, w), dtype=np.uint8)
mask[100:200, 100:250] = 1
added = cv.add(image1, image2, mask=mask) # 使用mask参数对[100:200, 100:250]部分进行add操作,其余位置补0
subbed = cv.subtract(image2, image1, mask=mask) # 使用mask参数对[100:200, 100:250]部分进行sub操作,其余位置补0
cv.imshow("added", added)
cv.imshow("subbed", subbed)
cv.waitKey(0)
cv.destroyAllWindows()
结果示例:
学习参考
本系列所有OpenCv相关的代码示例和内容均来自博主学习的网站:opencv_course