您当前的位置:首页 > 计算机 > 编程开发 > Python

python --opencv图像处理详解(阈值、二值化、翻转、缩放、平移、融合)

时间:08-16来源:作者:点击数:
CDSY,CDSY.XYZ

python --opencv图像处理详解(阈值、二值化、翻转、缩放、平移、融合)

demo

官方网站: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()

OpenCV 入门

1. 读入图像

读取图像是通过函数 cv.imread() 实现

img = cv.imread(文件名,[,参数])

第二个参数是一个标志,它指定了读取图像的方式。

  • cv.IMREAD_COLOR: 加载彩色图像,任何图像的透明度都会被忽视,如果不传参数,这个值是默认值。
  • cv.IMREAD_GRAYSCALE:以灰度模式加载图像。
  • cv.IMREAD_UNCHANGED:加载图像,包括alpha通道

注意:这三个标志可以简化为 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) 必须要加,如果不等待输入,整个窗体将会一闪而过。

像素处理与 Numpy 操作以及 Matplotlib 显示图像

普通操作

  1. 读取像素

读取像素可以通过行坐标和列坐标来进行访问,灰度图像直接返回灰度值,彩色图像则返回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()
# 对于读取灰度图像的像素值,只会返回相应的灰度。
在这里插入图片描述

2. 彩色图像读取操作:

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
  1. 修改像素

修改像素需要使用到 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 的转换。

图像属性、图像感兴趣 ROI 区域及通道处理

图像属性

图像属性包括行数,列数和通道数,图像数据类型,像素数等。

  1. 形状: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)
    
  2. 像素数量: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
    
  3. 图像类型-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 抠过图的人应该都清楚抠图的时候可以使用单通道进行抠图操作。

  1. 将图像的通道拆分出来可以使用 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索引。
  2. 合并图像通道
    合并图像通道我们使用函数 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.cdsy.xyz/computer/programme/algorithm/240816/cd62739.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)
  • retval: 阈值
  • dst: 处理后的图像
  • src: 原图像
  • thresh: 阈值
  • maxval: 最大值
  • type: 处理类型

常用的 5 中处理类型如下:

  • cv.THRESH_BINARY: 二值处理
  • cv.THRESH_BINARY_INV: 反二值处理
  • cv.THRESH_TRUNC: 截断阈值化
  • cv.THRESH_TOZERO: 阈值化为 0
  • cv.THRESH_TOZERO_INV: 反阈值化为 0

接下来这几种处理类型有啥不同,我们一个一个来看。

二值处理

这种二值处理方式最开始需要选定一个阈值 T ,从 0 ~ 255 之间,我这里选择出于中间的那个数 127 。

接下来的处理规则就是这样的:

  • 大于等于 127 的像素点的灰度值设定为最大值,也就是 255 白色
  • 小于 127 的像素点的灰度值设定为 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_BINARY)

# 显示图像
cv.imshow("src", src)
cv.imshow("result", b)

# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()
在这里插入图片描述

反二值处理

这种方式和上面的二值处理非常相似,只是把处理规则给反了一下:

  • 大于等于 127 的像素点的灰度值设定为 0 ,也就是白色
  • 小于 127 的像素点的灰度值设定为最大值,也就是 255 白色
    完整代码如下:

示例

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()
在这里插入图片描述

这个方法是亮的部分不改,把比较暗的部分修改为 0 。

全家福

接下来还是给这几种阈值处理后的图像来个全家福,让大家能有一个直观的感受,代码我也给出来,如下:

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()
在这里插入图片描述
CDSY,CDSY.XYZ
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐