python --opencv图像处理详解(阈值、二值化、翻转、缩放、平移、融合)_opencs-程序员宅基地

技术标签: 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

这里的 alphabeta 都是系数,而 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)
  • 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()

在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_44634704/article/details/121765067

智能推荐

iOS代码混淆-从入门到放弃-程序员宅基地

文章浏览阅读663次,点赞16次,收藏18次。代码混淆是指将程序中的方法名、属性名等符号重命名,并对代码进行改写,使其加密和混淆,增加应用逆向工程的难度。在移动互联网时代,代码混淆越来越受到开发者的重视。iOS代码混淆可以提高难度,从而防止应用程序被盗用或反编译,保护开发者的权益。但是同时也带来了一些问题,例如混淆后的函数名可能会影响代码的可维护性。因此,在使用代码混淆时需要进行合理规划。

【51单片机】单片机定时器与串口通信-程序员宅基地

文章浏览阅读625次,点赞9次,收藏10次。数码管是一种能够显示数字的电子元件,常见于数字时钟、计时器、温度计、电子秤等设备中。它通常由多个发光二极管(LED)或荧光显示管(VFD)组成,能够显示从0到9的数字。a.数码管由多个数字或字符的显示单元组成,每个显示单元可以显示一个数字或字符。b.每个显示单元内部包含多个LED或者VFD,排列成特定的数字或字符形状,以便显示。a.数码管通过控制各个LED或VFD的点亮状态来显示数字。b.当所需数字被显示时,控制电路会向相应的LED或VFD施加电压,使其发光,从而形成数字的形状。

ICCV 2019 PAMTRI: Pose-Aware Multi-Task Learning for Vehicle Re-Identification-程序员宅基地

文章浏览阅读1.6k次。文章目录摘要1.引言2.相关工作2.1 Vehicle ReID2.2 Vehicle pose estimation2.3 Synthetic data3.提出的方法3.1 随机合成数据集3.2 车辆姿态估计3.3 车辆重识别的多任务学习4.评估4.1 数据集和评估协议4.2 实现细节4.3 与其他方法的比较4.4 属性分类比较4.5 车辆姿态估计比较5.结论PAMTRI:Pose-Aware..._pamtri

spark DataFrame 保存成csv 使用execl打开乱码解决方法_spark excel 中文-程序员宅基地

文章浏览阅读4k次,点赞3次,收藏12次。工作中需要通过Spark以csv格式输出spark计算结果包括一些指标和维度为了方便普通客户使用表头要求中文。中文识别上遇到了些问题,出现了乱码:解决csv文件中的乱码最直接的思路就是添加BOM,这样Excel在打开Excel的时候就知道使用什么样的编码来解析这篇文档了。方法如下 只需要在csv文件表头字段名称的第一个字段名称的字符串最前面拼接一个BOM字符串就可以了..._spark excel 中文

1024,程序员节_1024程序代码-程序员宅基地

文章浏览阅读1.3w次,点赞23次,收藏11次。1024程序员节是中国广大程序员的共同节日。1024是2的十次方,二进制计数的基本计量单位之一。程序员(英文Programmer)是从事前端、后端程序开发、系统运维、测试等的专业人员。程序员就像是一个个1024,以最低调、踏实、核心的功能模块搭建起这个科技世界。1G=1024M,而1G与1级谐音,也有一级棒的意思。以下表情包,博君一笑程序员的高级自黑每当想放松的时..._1024程序代码

脱贫帮扶绩效评价-2020年华数杯C题(含python代码)-程序员宅基地

文章浏览阅读1.1w次,点赞33次,收藏20次。支持博主,请关注公众号个人公众号 第一问就是个相关性分析。具体而言,就是计算2015年和2020年各个指标的相关性。例如:2015年和2020年的SR这个指标的相关性是多少,计算方式用皮尔森方法即可。下面是代码```python# -*- coding: utf-8 -*-import pandas as pdimport matplotlib.pyplot as plt# 原始数据的问_2020年华数杯c题

随便推点

Linux tracepoint 简介-程序员宅基地

文章浏览阅读2.4k次,点赞5次,收藏12次。Linux tracepoint 简介和相应的实现原理_tracepoint

Java视觉智能识别_java 视觉模块-程序员宅基地

文章浏览阅读166次。JavaVision是一个基于Java开发的全能视觉智能识别项目,不仅实现PaddleOCR-V4、YoloV8物体识别、人脸识别、以图搜图等核心功能,还可以轻松扩展到其他领域,如语音识别、动物识别、安防检查等。这使得JavaVision成为一个全面解决多种场景需求的自适应平台。_java 视觉模块

基于FPGA的按键消抖_fpga按键消抖-程序员宅基地

文章浏览阅读3.6k次。fpga实现按键消抖_fpga按键消抖

mysql时间索引和格式探讨_mysql 时间索引-程序员宅基地

文章浏览阅读5.3k次,点赞6次,收藏7次。mysql几种时间和索引探讨_mysql 时间索引

2023年东南大学网络空间安全学院保研夏令营面试经验贴_东南大学网安院夏令营-程序员宅基地

文章浏览阅读792次。分享东南网安的保研经验和给出一些建议_东南大学网安院夏令营

python解多元一次方程组_多元一次线性方程组的解法 代码-程序员宅基地

文章浏览阅读7.3k次,点赞2次,收藏22次。1.python解多元一次方程组import numpy as np#多元线性方程组# x-2y+3z=-10# 2x+3y+4z=5# 3x+5y+7z=7A=np.array([[1,-2,3],[2,3,4],[3,5,7]])b=np.array([-10,5,7])x=np.linalg.solve(A,b)print('三元一次方程组的解:\n',x)结果如下:2...._多元一次线性方程组的解法 代码

推荐文章

热门文章

相关标签