官方网站:https://opencv.org/
GitHub:https://github.com/opencv/opencv
官方文档:https://docs.opencv.org/
中文文档(非官方):http://www.woshicver.com/
官方 Demo :https://github.com/opencv/opencv/blob/master/samples/python
OpenCV在Python中有两个类库,一个是opencs-python,另一个是opencv-contrib-python。
opencv-python是只包含了主要模块的包,而opencv-contrib-python包含了主要模块以及一i写扩展模块,带一些收费或者专利的算法,还有一些比较新的算法的高级版本。
# opencv-python 安装命令
pip install opencv-python
’
# opencv-contrib-python
pip install opencv-contrib-python
安装完成后可以通过以下代码查看安装的版本信息:
import cv2 as cv
# 查看版本信息
print(cv.getVersionString())
# 输出结果
4.2.0
【ImportError: libXext.so.6: cannot open shared object file: No such file or directory】
在Centos系统中,当你使用pip install opencv-python,import cv2时,如果以上错误,请运行:
yum install libXext
图像都是由像素( pixel )构成的,就像下面的这种小方格:
这些小方格每一个都有自己明确的位置和被分配的色彩值,而这些小方格的颜色和位置就决定了这个图像所呈现出来的样子。
像素是图像中最小的单位,每一个点阵图像包含了一定量的像素,这些像素决定图像在屏幕上所呈现的大小。
图像通常包括有 二值图像 、 灰度图像 和 彩色图像 。
二值图像
二值图像就是在图像中,任何一个点非黑即白,像素要么为 255 (白色) 要么为 0 (黑色) 。转换的时候一般将像素 >=127 的设置为白色,其余的设置为黑色。
灰度图像是除了黑白之外,还添加了第三种颜色:灰色,灰色把灰度划分为 256 个不同的亮度,例如纯白色,它的亮度级别是255。
图像转化为灰度图像有以下几种算法:
浮点算法:Gray = R 0.3 + G 0.59 + B * 0.11
整数方法:Gray = ( R 30 + G 59 + B * 11 ) / 100
移位方法:Gray = ( R 76 + G 151 + B * 28 ) >> 8
平均值法:Gray = ( R + G + B ) / 3
仅取绿色:Gray = G
加权平均值算法:R = G = B = R 0.299 + G 0.587 + B * 0.144
彩色图像
彩色图像是RGB图像,RGB表示红、绿、蓝三原色,计算机里所有颜色都是三原色不同比例组成的,即三色通道。
常用图像示例代码
import cv2 as cv
# 读取图像
img = cv.imread("maliao.jpg", cv.IMREAD_COLOR)
cv.imshow("read_img", img)
# 灰度图像
img_gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
cv.imshow("gray_img",img_gray)
# 二值图像
ret, binary = cv.threshold(img_gray, 127, 255, cv.THRESH_BINARY)
cv.imshow("binary_img", binary)
cv.waitKey()
1. 读入图像
读取图像是通过函数 cv.imread() 实现
img = cv.imread(文件名,[,参数])
第二个参数是一个标志,它指定了读取图像的方式。
注意:这三个标志可以简化为 1 、 0 、 -1 。
2. 显示图像
cv.imshow(窗口名, 图像名)
3. 窗口等待
cv.waitKey() 是一个键盘绑定函数。其参数是以毫秒为单位的时间。该函数等待任何键盘事件指定的毫秒。如果您在这段时间内按下任何键,程序将继续运行。如果 0 被传递,它将无限期地等待一次敲击键。
cv.waitKey(delay)
4. 删除窗口
cv.destroyAllWindows() 删除所有窗口
cv.destroyWindows() 删除指定的窗口
5. 写入图像
cv.imwrite(文件地址, 文件名)
代码示例
我们读取一张图片,将这张图片显示出来后,再将这张图片保存起来。
import cv2 as cv
# 读取图片
img = cv.imread("maliao.jpg", 1)
# 显示图片
cv.imshow("demo", img)
# 等待输入
cv.waitKey(0)
cv.destroyAllWindows()
# 图片写入
cv.imwrite("demo.jpg", img)
这里需要注意的是 cv.waitKey(0) 必须要加,如果不等待输入,整个窗体将会一闪而过。
普通操作
读取像素可以通过行坐标和列坐标来进行访问,灰度图像直接返回灰度值,彩色图像则返回B、G、R三个分量。
需要注意的是, OpenCV 读取图像是 BGR 存储显示。
灰度图片读取操作:
import cv2 as cv
# 灰度图像读取
gray_img = cv.imread("maliao.jpg", cv.IMREAD_GRAYSCALE)
print(gray_img[20, 30])
# 显示图片
cv.imshow("gray_img", gray_img)
# 等待输入
cv.waitKey()
cv.destroyAllWindows()
# 对于读取灰度图像的像素值,只会返回相应的灰度。
import cv2 as cv
# 彩色图像读取
color_img = cv.imread("maliao.jpg", cv.IMREAD_COLOR)
print(color_img[20, 30])
blue = color_img[20, 30, 0]
print(blue)
green = color_img[20, 30, 1]
print(green)
red = color_img[20, 30, 2]
print(red)
# 显示图片
cv.imshow("color_img", color_img)
# 等待输入
cv.waitKey()
cv.destroyAllWindows()
需要注意的是在获取彩色图片像素时的第二个参数 1|2|3 的含义是获取 BGR 三个通道的像素。
# 打印结果
[ 3 2 236]
3
2
236
修改像素
修改像素时,直接对像素赋值新像素即可。
如果是灰度图片,直接赋值即可。
如果是彩色图片,则需依次给 BGR 三个通道的像素赋值。
import cv2 as cv
# 灰度图像读取
gray_img = cv.imread("maliao.jpg", cv.IMREAD_GRAYSCALE)
print(gray_img[20, 30])
# 像素赋值
gray_img[20, 30] = 255
print(gray_img[20, 30])
# 打印结果
72
255
# 彩色图像读取
color_img = cv.imread("maliao.jpg", cv.IMREAD_COLOR)
print(color_img[20, 30])
# 像素依次赋值
color_img[20, 30, 0] = 255
color_img[20, 30, 1] = 255
color_img[20, 30, 2] = 255
print(color_img[20, 30])
# 打印结果
[ 3 2 236]
[255 255 255]
如果觉得依次对 BGR 三个通道赋值有些麻烦的话,也可以通过数组直接对像素点一次赋值:
# 像素一次赋值
color_img[20, 30] = [0, 0, 0]
print(color_img[20, 30])
# 打印结果
[0 0 0]
下面是对一个区域的像素进行赋值,将这个区域的像素全都赋值成为白色:
import cv2 as cv
color_img = cv.imread("maliao.jpg", cv.IMREAD_COLOR)
color_img[50:100, 50:100] = [255, 255, 255]
cv.imshow("color_img", color_img)
cv.waitKey()
cv.destroyAllWindows()
使用 Numpy 操作
1.读取像素
使用 Numpy 进行像素读取,调用方式如下:
返回值 = 图像.item(位置参数)
读取灰度图像和彩色图像如下:
import cv2 as cv
# 读取灰度图像
gray_img = cv.imread("maliao.jpg", cv.IMREAD_GRAYSCALE)
print(gray_img.item(20, 30))
# 打印结果
72
# 读取彩色图像
color_img = cv.imread("maliao.jpg", cv.IMREAD_COLOR)
blue = color_img.item(20, 30, 0)
print(blue)
green = color_img.item(20, 30, 1)
print(green)
red = color_img.item(20, 30, 2)
print(red)
# 打印结果
3
2
236
修改像素需要使用到 Numpy 的 itemset() 方法,调用方式如下:
图像.itemset(位置, 新值)
下面是我将 [20, 30] 这个修改为白色的示例:
import cv2 as cv
# 读取彩色图像
color_img = cv.imread("maliao.jpg", cv.IMREAD_COLOR)
print(color_img[20, 30])
color_img.itemset((20, 30, 0), 255)
color_img.itemset((20, 30, 1), 255)
color_img.itemset((20, 30, 2), 255)
print(color_img[20, 30])
# 输出结果
[ 3 2 236]
[255 255 255]
注意:普通操作通常用于选择数组的区域,例如上面的示例中的选择了 [50:100, 50:100] 这么一个正方形。对于单个像素访问, Numpy 数组方法 array.item() 和 array.itemset() 被认为更好。
Matplotlib 显示图像
我们可以通过 OpenCV 读入图像,然后使用 Matplotlib 来进行图像显示。
import cv2 as cv
from matplotlib import pyplot as plt
img=cv.imread('maliao.jpg', cv.IMREAD_COLOR)
plt.imshow(img)
plt.show()
如果我们直接使用 Matplotlib 来显示 OpenCV 读入的图像,会得到下面这个蓝色的马里奥:
这是因为对于 OpenCV 的像素是 BGR 顺序,然而 Matplotlib 所遵循的是 RGB 顺序。
解决的方案有很多种(循环像素点的不算哈,这个太傻了),如下:
import cv2 as cv
from matplotlib import pyplot as plt
img=cv.imread('maliao.jpg',cv.IMREAD_COLOR)
# method1
b,g,r=cv.split(img)
img2=cv.merge([r,g,b])
plt.imshow(img2)
plt.show()
# method2
img3=img[:,:,::-1]
plt.imshow(img3)
plt.show()
# method3
img4=cv.cvtColor(img, cv.COLOR_BGR2RGB)
plt.imshow(img4)
plt.show()
结果我就不贴了,这三种方法都可以完成 BGR 至 RGB 的转换。
图像属性
图像属性包括行数,列数和通道数,图像数据类型,像素数等。
形状:shape
图像的形状可以通过 shape 关键字进行获取,使用 shape 关键的后,获取的信息包括行数、列数、通道数的元祖。
需要注意的是,如果是灰度图片,只会返回图像的行数和列数,而彩色图片才会图像的行数、列数和通道数。
import cv2 as cv
# 读取彩色图片
color_img = cv.imread("maliao.jpg", cv.IMREAD_ANYCOLOR)
print(color_img.shape)
# 结果打印
(310, 560, 3)
# 读取灰度图片
gray_img = cv.imread("maliao.jpg", cv.IMREAD_GRAYSCALE)
print(gray_img.shape)
# 结果打印
(310, 560)
像素数量:size
图像的像素数量可以通过关键字 size 进行获取。
同样需要注意的是,灰度图片的像素数量是要小于彩色图片的,具体的关系是 1/3 。
import cv2 as cv
# 读取彩色图片
color_img = cv.imread("maliao.jpg", cv.IMREAD_ANYCOLOR)
print(color_img.size)
# 结果打印
520800
# 读取灰度图片
gray_img = cv.imread("maliao.jpg", cv.IMREAD_GRAYSCALE)
print(gray_img.size)
# 结果打印
173600
图像类型-dtype
图像类型是通过关键字 dtype 获取的,通常返回 uint8 ,这个属性在彩色图片和灰度图片中是保持一致的。
注意 dtype 在调试时非常重要,因为 OpenCV-Python 代码中的大量错误是由无效的数据类型引起的。
import cv2 as cv
# 读取彩色图片
color_img = cv.imread("maliao.jpg", cv.IMREAD_ANYCOLOR)
print(color_img.dtype)
# 结果打印
uint8
# 读取灰度图片
gray_img = cv.imread("maliao.jpg", cv.IMREAD_GRAYSCALE)
print(gray_img.dtype)
# 结果打印
uint8
获取图像感兴趣 ROI 区域
ROI(Region of Interest)表示感兴趣区域。
它是指从被处理图像以方框、圆形、椭圆、不规则多边形等方式勾勒出需要处理的区域。可以通过各种算子(Operator)和函数求得感兴趣ROI区域,并进行图像的下一步处理,被广泛应用于热点地图、人脸识别、图像分割等领域。
如果我们要对于图像中的眼睛检测,首先对整个图像进行人脸检测。在获取人脸图像时,我们只选择人脸区域,搜索其中的眼睛,而不是搜索整个图像。它提高了准确性(因为眼睛总是在面部上:D )和性能(因为我们搜索的区域很小)。
我们通过像素矩阵可以直接得到 ROI 区域,如: img[200:400, 200:400] 。
比如下面这个示例我们获取马里奥的脸,然后再把它显示出来:
import cv2 as cv
img = cv.imread("maliao.jpg", cv.IMREAD_UNCHANGED)
face = img[10:175, 100:260]
# 原始图像显示
cv.imshow("demo", img)
# 马里奥的脸显示
cv.imshow("face", face)
#等待显示
cv.waitKey(0)
cv.destroyAllWindows()
如果我们要把这两张图像合成一张图像,可以对图像进行区域赋值:
import cv2 as cv
img = cv.imread("maliao.jpg", cv.IMREAD_UNCHANGED)
# 获取 ROI 区域
face = img[10:175, 100:260]
# 图像赋值
img[0:165, 0:160] = face
# 原始图像显示
cv.imshow("demo", img)
#等待显示
cv.waitKey(0)
cv.destroyAllWindows()
这里我稍微偷点懒,直接就把 ROI 区域放在了图片的左上角,这个位置可以随意指定,但是指定的区域要和 ROI 的区域一样大,否则会报一个 ValueError 的错误。
拆分和合并图像通道
有些时候,我们需要分别处理图像的 B,G,R 通道。的通道,用 PS 抠过图的人应该都清楚抠图的时候可以使用单通道进行抠图操作。
将图像的通道拆分出来可以使用 split() 函数,如下:
import cv2 as cv
img = cv.imread("maliao.jpg", cv.IMREAD_UNCHANGED)
#拆分通道
b, g, r = cv.split(img)
# 分别显示三个通道的图像
cv.imshow("B", b)
cv.imshow("G", g)
cv.imshow("R", r)
# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()
可以看到,三个通道的图像看起来都是灰白色的,这个玩过 PS 的人应该都很熟悉。
除了使用 split() 函数获取图像通道,还可以通过索引进行获取,代码如下:
b = img[:, :, 0]
g = img[:, :, 1]
r = img[:, :, 2]
注意: split() 函数是一项耗时的操作(就时间而言)。因此,仅在必要时才这样做。否则请进行Numpy索引。
合并图像通道
合并图像通道我们使用函数 merge() ,示例如下:
import cv2 as cv
img = cv.imread("maliao.jpg", cv.IMREAD_UNCHANGED)
# 拆分通道
b, g, r = cv.split(img)
# 合并图像通道
m = cv.merge([r, g, b])
cv.imshow('merge', m)
# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()
这里如果是按照 [r, g, b] 进行图像通道合并,我们的马里奥就会变身成为蓝精灵,因为 OpenCV 是按照 BGR 读取的,如果想要显示会原图,合并的时候也按照 [b, g, r] 合并即可,如下:
如果我们想要做一个真正的蓝精灵,可以只提取 B 颜色通道,其余两个 G 、 R 通道全部设置为 0 ,这样,我们就获得了一个真正的蓝精灵(整个图像只有蓝色通道),代码如下:
import cv2 as cv
import numpy as np
# 读取图片
img = cv.imread("maliao.jpg", cv.IMREAD_UNCHANGED)
rows, cols, chn = img.shape
# 拆分通道
b = img[:, :, 0]
g = np.zeros((rows,cols), dtype=img.dtype)
r = np.zeros((rows,cols), dtype=img.dtype)
# 合并图像通道
m = cv.merge([b, g, r])
cv.imshow('merge', m)
# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()
图像加法
图像加法有两种方式,一种是通过 Numpy 直接对两个图像进行相加,另一种是通过 OpenCV 的 add() 函数进行相加。
不管使用哪种方法,相加的两个图像必须具有相同的深度和类型,简单理解就是图像的大小和类型必须一致。
Numpy 加法
Numpy 的运算方法是: img = img1 + img2 ,然后再对最终的运算结果取模。
当最终的像素值 <= 255 时,则运算结果直接为 img1 + img2 。
当最终的像素值 > 255 时,则运算的结果需对 255 进行取模运算。
OpenCV 加法
OpenCV 的运算方式是直接调用 add() 函数进行的,这时的运算方式是饱和运算。
当最终的像素值 <= 255 时,则运算结果直接为 img1 + img2 。
当最终的像素值 > 255 时,这时则是饱和运算,结果固定为 255 。
两种加法方式对应的示例如下:
import cv2 as cv
# 读取图像
img = cv.imread("maliao.jpg", cv.IMREAD_UNCHANGED)
test = img
# Numpy 加法
result1 = img + test
# OpenCV 加法
result2 = cv.add(img, test)
# 显示图像
cv.imshow("img", img)
cv.imshow("result1", result1)
cv.imshow("result2", result2)
# 等待显示
cv.waitKey()
cv.destroyAllWindows()
可以看到,使用 Numpy 取模加法的图片整体更偏绿色,而使用 OpenCV 饱和运算的加法,整体颜色更偏白色。
图像融合
图像融合其实也是一种图像加法,但是它和图像加法不同的是对图像赋予不同的权重,可以使图像具有融合或者透明的感觉。
图像加法: img = img1 + img2
图像融合: img = img1 * alpha + img2 * beta + gamma
图像融合用到的函数为 addWeighted()
具体如下:
dst = cv.addWeighter(img1, alpha, img2, beta, gamma)
dst = img1 * alpha + img2 * beta + gamma
这里的 alpha
和 beta
都是系数,而 gamma
则是一个亮度调节量,不可省略。
下面这个示例中,我又找了一张下雨的图片,用这张图片和马里奥做一个图像融合的案例:
import cv2 as cv
# 读取图像
img1 = cv.imread("maliao.jpg", cv.IMREAD_UNCHANGED)
img2 = cv.imread("rain.jpg", cv.IMREAD_UNCHANGED)
# 图像融合
img = cv.addWeighted(img1, 0.4, img2, 0.6, 10)
# 显示图像
cv.imshow("img1", img1)
cv.imshow("img2", img2)
cv.imshow("img", img)
# 等待显示
cv.waitKey()
cv.destroyAllWindows()
图像融合时需要注意的和上面一致,需要图像大小是相等的,上面的示例这两张图片都是像素为 560 * 310 且都为 RGB 的图片。
改变颜色空间
OpenCV 中有超过150种颜色空间转换方法。我们先介绍两种最常用的:BGR <-> 灰度 和 BGR <-> HSV 。
对于改变颜色空间,我们使用 cvtColor(input_image, flag) 函数,其中的 flag 为转换的类型。
一些常见的 flag 值:
# BGR 转 灰度
cv.COLOR_BGR2GRAY
# BGR 转 HSV
cv.COLOR_BGR2HSV
# BGR 转 RGB
cv.COLOR_BGR2RGB
# 灰度 转 BGR
cv.COLOR_GRAY2BGR
可以很清楚的看到, flag 的命名非常的通俗易懂,如果想要获取其他所有的标记,可以使用下面这段代码:
import cv2 as cv
flags = [i for i in dir(cv) if i.startswith('COLOR_')]
print(flags)
# 结果就不贴了,挺长的。
注意:HSV 的色相范围为 [0,179] ,饱和度范围为 [0,255] ,值范围为 [0,255]
。不同的软件使用不同的范围。因此,如果需要将 OpenCV 值和它们比较,则需要将这些范围标准化。
我们使用 cvtColor()
这个函数将马里奥转化成灰度图像,示例如下:
import cv2 as cv
# 读取图像
img = cv.imread("maliao.jpg", cv.IMREAD_UNCHANGED)
# 图像类型转换
result = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
# 图像展示
cv.imshow("img", img)
cv.imshow("result", result)
# 等待显示
cv.waitKey()
cv.destroyAllWindows()
图像缩放只是调整图像的大小,为此, OpenCV 为我们提供了一个函数 cv.resize()
,原函数如下:
resize(src, dsize, dst=None, fx=None, fy=None, interpolation=None)
src 表示的是输入图像,而 dsize 代表的是输出图像的大小,如果为 0 ,则
dsize = Size(round(fx * src.cols), round(fx * src.rows))
dsize 和 fx 、 fy 不能同时为 0 。
fx 、 fy 是沿 x 轴和 y 轴的缩放系数,默认取 0 时,算法如下:
fx = (double) dsize.width / src.cols
fy = (double) dsize.height / src.rows
最后一个参数 interpolation 表示插值方式:
INTER_NEAREST - 最近邻插值
INTER_LINEAR - 线性插值(默认)
INTER_AREA - 区域插值
INTER_CUBIC - 三次样条插值
INTER_LANCZOS4 - Lanczos插值
示例:
import cv2 as cv
#读取图片
src = cv.imread('maliao.jpg')
print(src.shape)
#图像缩放
result = cv.resize(src, (300, 150))
print(result.shape)
#显示图像
cv.imshow("src", src)
cv.imshow("result", result)
#等待显示
cv.waitKey()
cv.destroyAllWindows()
需要注意的是,这里的 (300, 150)
设置的是 dsize 的列数为 300 ,行数为 150 。
同理,我们可以通过设定一个比例进行缩放,可以是等比例缩放,也可以是不等比例缩放,下面是等比例缩放的示例:
import cv2 as cv
# 设定比例
scale = 0.5
#读取图片
src = cv.imread('maliao.jpg')
rows, cols = src.shape[:2]
#图像缩放
result = cv.resize(src, ((int(cols * scale), int(rows * scale))))
print(result.shape)
#显示图像
cv.imshow("src", src)
cv.imshow("result", result)
#等待显示
cv.waitKey()
cv.destroyAllWindows()
除了可通过设定 dszie 对图像进行缩放,我们还可以通过设定 fx 和 fy 对图像进行缩放:
import cv2 as cv
#读取图片
src = cv.imread('maliao.jpg')
print(src.shape)
#图像缩放
result = cv.resize(src, None, fx=0.5, fy=0.5)
print(result.shape)
#显示图像
cv.imshow("src", src)
cv.imshow("result", result)
#等待显示
cv.waitKey()
cv.destroyAllWindows()
图像平移是通过仿射函数 warpAffine()
来实现的,原函数如下:
warpAffine(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)
在图像平移中我们会用到前三个参数:
src 需要变换的原始图像
M移动矩阵M
dsize变换的图像大小(如果这个大小不和原始图像大小相同,那么函数会自动通过插值来调整像素间的关系)。
图像的平移是沿着 x 方向移动 tx 距离, y 方向移动 ty 距离,那么需要构造移动矩阵:
我们通过 Numpy 来产生这个矩阵(必须是float类型的),并将其赋值给仿射函数 warpAffine()
,下面来看个示例:
import cv2 as cv
import numpy as np
#读取图片
src = cv.imread('maliao.jpg')
rows, cols = src.shape[:2]
# 定义移动距离
tx = 50
ty = 100
# 生成 M 矩阵
affine = np.float32([[1, 0, tx], [0, 1, ty]])
dst = cv.warpAffine(src, affine, (cols, rows))
# 显示图像
cv.imshow('src', src)
cv.imshow("dst", dst)
# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()
注意: warpAffine
函数的第三个参数是输出图像的大小,我这里设置的大小是原图片的大小,所以结果会有部分遮挡。
图像旋转主要调用 getRotationMatrix2D()
函数和 warpAffine()
函数实现,绕图像的某一个中心点旋转,具体如下:
M = cv2.getRotationMatrix2D((cols/2, rows/2), 30, 1)
参数分别为:旋转中心、旋转度数、scale
rotated = cv2.warpAffine(src, M, (cols, rows))
参数分别为:原始图像、旋转参数、原始图像宽高
图像旋转:设( x0 , y0 )是旋转后的坐标,( x , y )是旋转前的坐标,( m , n )是旋转中心, a 是旋转的角度(顺时针),( left , top )是旋转后图像的左上角坐标,则公式如下:
上面这个公式具体的推导过程可以参考这篇文章:https://www.cnblogs.com/xuanyuyt/p/7112876.html 。
示例
import cv2 as cv
#读取图片
src = cv.imread('maliao.jpg')
# 原图的高、宽
rows, cols = src.shape[:2]
# 绕图像的中心旋转
# 参数:旋转中心 旋转度数 scale
M = cv.getRotationMatrix2D((cols/2, rows/2), 90, 1)
#
dst = cv.warpAffine(src, M, (cols, rows))
# 显示图像
cv.imshow("src", src)
cv.imshow("dst", dst)
# 等待显示
cv.waitKey()
cv.destroyAllWindows()
第一个图像翻转,这个可是制作表情包的利器。
图像翻转在 OpenCV 中调用函数 flip()
实现,原函数如下:
flip(src, flipCode, dst=None)
src:原始图像。
flipCode:翻转方向,如果 flipCode 为 0 ,则以 X 轴为对称轴翻转,如果 fliipCode > 0 则以 Y 轴为对称轴翻转,如果 flipCode < 0 则在 X 轴、 Y 轴方向同时翻转。
示例
import cv2 as cv
import matplotlib.pyplot as plt
# 读取图片 由 GBR 转 RGB
img = cv.imread('maliao.jpg')
src = cv.cvtColor(img, cv.COLOR_BGR2RGB)
# 图像翻转
# flipCode 为 0 ,则以 X 轴为对称轴翻转,如果 fliipCode > 0 则以 Y 轴为对称轴翻转,如果 flipCode < 0 则在 X 轴、 Y 轴方向同时翻转。
img1 = cv.flip(src, 0)
img2 = cv.flip(src, 1)
img3 = cv.flip(src, -1)
# plt 显示图形
titles = ['Source', 'Ima1', 'Ima2', 'Ima3']
images = [src, img1, img2, img3]
for i in range(4):
plt.subplot(2, 2, i + 1)
plt.imshow(images[i])
plt.title(titles[i])
plt.xticks([])
plt.yticks([])
plt.show()
看到这个词可能大家都很懵,为啥在图像处理里面还会有阈值。
图像的阈值处理用大白话讲就是将图像转化为二值图像(黑白图),目的是用来提取图像中的目标物体,将背景和噪声区分开(可以近似的认为除了目标全是噪声)。
通常会设定一个阈值 T ,通过 T 将图像的像素划分为两类:大于 T 的像素群和小于 T 的像素群。
首先可以先将图像转化为灰度图像,因为在灰度图像中,每个像素都只有一个灰度值用来表示当前像素的亮度。
接下来二值化处理可以将图像中的像素划分为两类颜色,一种是大于阈值 T 的,另一种是小于阈值 T 的。
比如最常见的二值图像:
当灰度值小于阈值 T 的时候,可以将其像素设置为 0 ,表示为黑色。
当灰度值大于阈值 T 的时候,可以将其像素设置为 255 ,表示为白色。
在 OpenCV 中,为我们提供了阈值函数 threshold() 来帮助我们实现二值图像的处理。
retval, dst = threshold(src, thresh, maxval, type, dst=None)
常用的 5 中处理类型如下:
接下来这几种处理类型有啥不同,我们一个一个来看。
二值处理
这种二值处理方式最开始需要选定一个阈值 T ,从 0 ~ 255 之间,我这里选择出于中间的那个数 127 。
接下来的处理规则就是这样的:
示例
import cv2 as cv
src = cv.imread("maliao.jpg")
# BGR 图像转灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# 二值图像处理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_BINARY)
# 显示图像
cv.imshow("src", src)
cv.imshow("result", b)
# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()
这种方式和上面的二值处理非常相似,只是把处理规则给反了一下:
示例
import cv2 as cv
src = cv.imread("maliao.jpg")
# BGR 图像转灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# 二值图像处理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_BINARY_INV)
# 显示图像
cv.imshow("src", src)
cv.imshow("result", b)
# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()
从图像上可以看到,颜色和上面的二值图像正好相反,大部分的位置都变成了白色。
截断阈值化
这种方法还是需要先选定一个阈值 T ,图像中大于该阈值的像素点被设定为该阈值,小于该阈值的保持不变。
import cv2 as cv
src = cv.imread("maliao.jpg")
# BGR 图像转灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# 二值图像处理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_TRUNC)
# 显示图像
cv.imshow("src", src)
cv.imshow("result", b)
# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()
这种方式实际上是把图片比较亮的像素处理成为阈值,其他部分保持不变。
阈值化为 0
这种方式还是需要先选定一个阈值 T ,将小于 T 的像素点设置为 0 黑色,其他的保持不变。
import cv2 as cv
src = cv.imread("maliao.jpg")
# BGR 图像转灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# 二值图像处理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_TOZERO)
# 显示图像
cv.imshow("src", src)
cv.imshow("result", b)
# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()
全家福
接下来还是给这几种阈值处理后的图像来个全家福,让大家能有一个直观的感受,代码我也给出来,如下:
import cv2 as cv
import matplotlib.pyplot as plt
# 读取图像
img=cv.imread('maliao.jpg')
lenna_img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
gray_img=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 阈值化处理
ret1, thresh1=cv.threshold(gray_img, 127, 255, cv.THRESH_BINARY)
ret2, thresh2=cv.threshold(gray_img, 127, 255, cv.THRESH_BINARY_INV)
ret3, thresh3=cv.threshold(gray_img, 127, 255, cv.THRESH_TRUNC)
ret4, thresh4=cv.threshold(gray_img, 127, 255, cv.THRESH_TOZERO)
ret5, thresh5=cv.threshold(gray_img, 127, 255, cv.THRESH_TOZERO_INV)
# 显示结果
titles = ['Gray Img','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [gray_img, thresh1, thresh2, thresh3, thresh4, thresh5]
# matplotlib 绘图
for i in range(6):
plt.subplot(2, 3, i+1), plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()