# 读取图像

对于彩色图像而言,-1 和 1 的效果是相同的。

img = cv2.imread("./images/img.png", 1)
print(img)
print(type(img))
print(img.shape)
'''python
<class 'numpy.ndarray'>
(720, 1152, 3)
'''

# 显示图像

显示图像

# 保存图像

二。图像处理基础
1. 图像的基本表示方法
1.1 二值图像
二值图像是指仅包含黑色和白色两种颜色的图像

灰度图像有 256 个灰度级,用数值区间 [0,255] 来表示,其中 255 表示为纯白色,0 表示为纯黑色
256 个灰度级的数值恰好可以用一个字节(8 位二进制值)来表示

3.1 彩色图像
在 RGB 色彩空间中,存在 R,G,B 三个通道,每个通道的数值范围在 [0,255] 之间,我们利用这三个色彩通道的组合表示颜色
三种颜色经过混合可以配出常见的 256×256×256=16777216 种颜色
在 OpenCV 中,通道的顺序是 B→G→R

2. 像素处理
2.1 二值图像及灰度图像

由于 OpenCV 没有二值图像这种数据类型,所以我们可以把二值图像理解为特殊的灰度图像
为了方便理解,我们首先使用 Numpy 库生成一个 8×8 大小的数组,来模拟黑色图像来进行处理

img=np.array(Image.open("./例.jpg").convert('L')) #为了方便接下来对灰度图的演示,我们使用灰度图的方式打开 "./ 例.jpg"
print(img)
pylab.imshow(img)
cv2.imwrite("./例—灰度图.jpg",img)

2.2 彩色图像

RGB 模式的彩色图在读入 OpenCV 内进行处理时,会按照方向依次读取 RGB 的 B,G,R 通道的像素点,其在 OpenCV 内以 BGR 模式的三维数组形式存储
例如:
img [0,0] 访问图像 img 第 0 行第 0 列像素点的 BRG 值 [B,G,R]
img [0,0,0] 访问 img 第 0 行第 0 列第 0 通道的像素值,会得到 B 通道第 0 行第 0 列的值
img [0,0,2] 访问 img 第 0 行第 0 列第 2 通道的像素值,会得到 R 通道第 0 行第 0 列的值
为了方便理解,我们首先使用 Numpy 生成一个 2×4×3 大小的数组,用它来模拟一幅彩色图像并对其进行简单处理

blue=np.zeros((300,300,3),dtype=np.uint8)
blue[:,:,0]=255
print("blue=",blue)
pylab.imshow(blue)

这时候就有小伙伴发现,展示出来的图片是红色而不是蓝色!!!
这是因为 pylab 的 imshow 是以 RGB 的方式展示出来的,而不是 cv2 的 BGR

2.3 感兴趣区域(ROI)

在图像处理的中,我们可能会对图像的某一个特定区域感兴趣,该区域被称为感兴趣的区域(ROI)。在设定感兴趣的区域 ROI 后,就可以对该区域进行整体操作。

a=cv2.imread("./例.jpg") #提取 lena 的面部
face=a[50:400,500:1000] # 前一个是纵轴,后一个是横轴,起始点为图像的左上角
face=cv2.cvtColor(face,cv2.COLOR_BGR2RGB) #将 BGR 转换成 RGB
pylab.imshow(face)
cv2.imwrite("lena_face.jpg",face)

打码

2.4 通道拆分

在 RGB 图像中,图像由 R,G,B 三个通道构成。需要注意的是,在 OpenCV 中,通道是按照 B,G,R 通道顺序存储的

2.5 通道合并

2.6 获取图像属性
其实就是 numpy 的数组的属性

三。图像运算
1. 图像加法运算
1.1 加号运算符

对于 + 号运算,当对图像 a,图像 b 进行加法求和时,遵循以下规则:

当某位置像素相加得到的数值小于 255 时,该位置数值为两图像该位置像素相加之和
当某位置像素相加得到的数值大于 255 时,该位置数值为两像素该位置像素相加之和对 256 取模

1.2 cv2.add () 函数

对于 cv2.add () 运算,当对图像 a,图像 b 进行加法求和时,遵循以下规则:
当某位置像素相加得到的数值小于 255 时,该位置数值为两图像该位置像素相加之和
当某位置像素相加得到的数值大于 255 时,该位置数值为 255

2. 图像加权运算

所谓图像加权和,就是在计算两幅图像的像素值之和时,将每幅图像的权重考虑进来,可以用公式表示为 dst=saturate (src1×α+src×β+γ)

3. 位平面分解

二进制位平面分解技术(BBD)
对于任何一个非负的整数都可以被一个二进制序列所表示,数学公式如下所示:

对于一般灰度图像而言,其灰度值通常是一个 0 到 255 之间的十进制数,那么每一个像素都可以由 8 位二进制序列唯一表示。因此,BBD 可以将灰度图像拆分成 8 个二进制平面。第一个位平面是由灰度图像中每一个像素的二进制表示的所有第一位所组成的,如下图我们展示了将一个 lena 图分解成八张位平面子图的效果。

下图展示了将 lena 图分解后,各个平面图的样子,其中(a)为 lena 的原图,从 (b) 到(i) 展示了从最低位平面到最高位平面,其中我们可以很明显发现第八张子图 (i) 最接近 lena 的原图,这也就说明了在组成一张图的过程中,高位的图往往起到了更加关键的作用。

七。图像平滑处理
1. 均值滤波
dst=cv2.blur (src,ksize,anchor,borderType)

dst 是返回值
src 是需要处理的图像
kszie 是滤波核 (卷积核) 的大小
anchor 是锚点,默认值是(-1,-1)一般无需更改
borderType 是边界样式,一般无需更改
一般情况下,使用 dst=cv2.blur (src,ksize) 即可
原理:它只取内核区域下所有像素的平均值并替换中心元素。3x3 标准化的盒式过滤器如下所示:

lena_noise=cv2.imread("噪声lena.jpg",0)
lena_deal=cv2.blur(lena_noise,(5,5))
lena_noise = np.concatenate((np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2)),
                         axis = -1)
lena_deal = np.concatenate((np.expand_dims(lena_deal, axis = 2), np.expand_dims(lena_deal, axis = 2), np.expand_dims(lena_deal, axis = 2)),
                         axis = -1)
pylab.subplot(1,2,1)
pylab.imshow(lena_noise)
pylab.subplot(1,2,2)
pylab.imshow(lena_deal)
pylab.show()

五。几何变换
1. 缩放

2. 翻转

3. 仿射
仿射变换是指图像可以通过一系列的几何变换来实现平移,旋转等多种操作。该变换能够保持图像的平直性和平行性。
平直性是指图像经过仿射变换后,直线仍然是直线;
平行性是指图像在完成仿射变换后,平行线仍然是平行线。

注意在上面中,变换矩阵的大小是固定的,为 2x3.

从上面的例子可以看出,在移动之后,原位置填充为 0。
且函数的最后一个参数为原图像大小的元组。

4. 旋转
在使用函数 cv2.warpAffine () 对图像进行旋转时,可以通过函数 cv2.getRotationMatrix2D () 获得转换矩阵。该函数的语法格式为:
retval=cv2.getRotationMatrix2D(centrer,angle,scale)
center 为旋转中心点(含有两个元素的元组表示)
angle 为旋转的角度,正数表示逆时针旋转,反之为逆时针旋转
scale 为变换尺度(缩放大小)

5. 更复杂的仿射变换
面对更复杂的仿射变换,OpenCV 提供了函数 cv2.getAffineTransform () 来生成放射函数 cv2.warpAffine () 所使用的转化矩阵 M。该函数的语法格式为
retval=cv2.getAffineTransform (src,dst)
src 代表输入图像的三个点的坐标
dst 代表输出图像的三个点的坐标

6. 重映射
把一幅图像的像素点放置到另一副图像内的指定位置,这个过程成为重映射。
OpenCV 提供了多种重映射方式,但是我们有时希望使用自定义的方式来完成重映射。
OpenCV 内的重映射函数 cv2.remap () 提供了更方便,更自由地映射方式,其语法格式如下:
dst=cv2.remap(src,map1,map2,interpolation[,borderMode[,borderValue]])
式中:
dst 代表目标图像,它和 src 具有相同的大小和类型
src 代表原始图像
map1 参数有两种可能的值:
-- 表示(x,y)点的一个映射
-- 表示 CV_16SC2,CV_32FC1,CV_32FC2 类型(x,y)点的 x 值。
map2 参数有两种可能的值:
--map1 表示(x,y)时,该值为空
--map1 表示(x,y)点的 x 值时,该值是 CV_16UC1,CV_32FC1 类型(x,y)点的 y 值。
Interpolation 代表插值方式,这里不支持 INTER_AREA 方法。
borderMode 代表边界方式。
borderValue 代表边界值,默认为 0

#例
img=np.random.randint(0,256,size=[4,5],dtype=np.uint8)#生成一个随机 4*5 的矩阵
rows,cols=img.shape
mapx=np.ones(img.shape,np.float32)*3
mapy=np.ones(img.shape,np.float32)*0
res=cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR) #res 的每一个位置都被设置成了 img [0][3]
print("img=\n",img)
print("mapx=\n",mapx)
print("mapy=\n",mapy)
print("rst='\n",res)

重映射尚未看完

六。阈值处理
阈值处理是值剔除图像内像素值高于一定值或低于一定值的像素点。例如,设定阈值为 127,然后:

将图像内所有像素值大于 127 的像素点的值设为 255
将图像内所有像素值小于或等于 127 的像素点的值设为 0
通过上述方法能够得到一幅 444 二值图像。如图 6-1 所示,按照上述阈值处理方法将一幅灰度图像处理为一幅二值图像,有效地实现了前景和背景的分离
OpenCV 提供了函数 cv2.threshold () 和函数 cv2.adaptive.Threshold (),用于实现阈值处理。

1.threshold 函数
retval,dst=cv2.threshold (src,shresh,maxval,type)
式中:

retval 代表返回的阈值
dst 代表阈值分割结果图像,与原始图像具有相同的大小和类型
src 代表要进行阈值分割的图像,可以是多通道的,8 位或 32 位浮点型数值
thresh 代表要设定的阈值
maxval 代表 type 参数位 THRESH_BINARY 或者 THRESH_BINARY_INV 类型时,需要设定的最大值
type 代表阈值分割的类型,具体内容如下图所示

尚未看完

七。图像平滑处理
1. 均值滤波
dst=cv2.blur (src,ksize,anchor,borderType)

dst 是返回值
src 是需要处理的图像
kszie 是滤波核 (卷积核) 的大小
anchor 是锚点,默认值是(-1,-1)一般无需更改
borderType 是边界样式,一般无需更改
一般情况下,使用 dst=cv2.blur (src,ksize) 即可
原理:它只取内核区域下所有像素的平均值并替换中心元素。3x3 标准化的盒式过滤器如下所示:

lena_noise=cv2.imread("噪声lena.jpg",0)
lena_deal=cv2.blur(lena_noise,(5,5))
lena_noise = np.concatenate((np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2)),
                         axis = -1)
lena_deal = np.concatenate((np.expand_dims(lena_deal, axis = 2), np.expand_dims(lena_deal, axis = 2), np.expand_dims(lena_deal, axis = 2)),
                         axis = -1)
pylab.subplot(1,2,1)
pylab.imshow(lena_noise)
pylab.subplot(1,2,2)
pylab.imshow(lena_deal)
pylab.show()

补充 np.expand_dims ()

(1, 2, 3) 中,1 表示第一个方括号内只有一个元素,即只有一个 [[1,2,3],[4,5,6]] ;2 表示第二个方括号内有两个元素,即为 [1,2,3] 和 [4,5,6];3 表示第三个方括号内有 3 个元素。

np.expand_dims (a, axis=0) 表示在 0 位置添加数据,转换结果如下:

在指定的维度插入之后,之前在此处的位置向后移动

np.expand_dims (a, axis=1) 表示在 1 位置添加数据,转换结果如下:

np.expand_dims (a, axis=2) 表示在 2 位置添加数据,转换结果如下:

np.expand_dims (a, axis=3) 表示在 3 位置添加数据,转换结果如下:

能在 (1,2,3) 中插入的位置总共为 4 个,再添加就会出现以下的警告,要不然也会在后面某一处提示 AxisError。
axis = -1 也就是相当于在最后插入

2. 方框滤波
二、cv2.boxFilter (img,-1,ksize,normalize=True) 方框滤波
说明:当 normalize=True 时,与均值滤波结果相同, normalize=False,表示对加和后的结果不进行平均操作,大于 255 的使用 255 表示。

lena_noise=cv2.imread("噪声lena.jpg",0)
lena_deal_1=cv2.boxFilter(lena_noise,-1,(5,5))
lena_deal_2=cv2.boxFilter(lena_noise,-1,(5,5),normalize=False)
lena_noise = np.concatenate((np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2)),
                         axis = -1)
lena_deal_1 = np.concatenate((np.expand_dims(lena_deal_1, axis = 2), np.expand_dims(lena_deal_1, axis = 2), np.expand_dims(lena_deal_1,axis = 2)),
                         axis = -1)
lena_deal_2= np.concatenate((np.expand_dims(lena_deal_2, axis = 2), np.expand_dims(lena_deal_2, axis = 2), np.expand_dims(lena_deal_2, axis = 2)),
                         axis = -1)
pylab.subplot(1,3,1)
pylab.imshow(lena_noise)
pylab.subplot(1,3,2)
pylab.imshow(lena_deal_1)
pylab.subplot(1,3,3)
pylab.imshow(lena_deal_2)
pylab.show()

3. 高斯滤波
cv2.GaussianBlur (img, ksize,sigmaX,sigmaY)
说明:sigmaX,sigmaY 分别表示 X,Y 方向的标准偏差。如果仅指定了 sigmaX,则 sigmaY 与 sigmaX 相同。如果两者都为零,则根据内核大小计算它们。
原理:核中区域贡献率与距离区域中心成正比,权重与高斯分布相关。

lena_noise=cv2.imread("噪声lena.jpg",0)
lena_deal=cv2.GaussianBlur(lena_noise,(5,5),0,0)
lena_noise = np.concatenate((np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2)),
                         axis = -1)
lena_deal = np.concatenate((np.expand_dims(lena_deal, axis = 2), np.expand_dims(lena_deal, axis = 2), np.expand_dims(lena_deal,axis = 2)),
                         axis = -1)
pylab.subplot(1,2,1)
pylab.imshow(lena_noise)
pylab.subplot(1,2,2)
pylab.imshow(lena_deal)
pylab.show()

4. 中值滤波
cv2.medianBlur (img, k)
说明:imgs 为原图像,k 为方框的尺寸,相当于将方框内的个值进行排序,取中值作为当前值。
原理:中心点的像素被核中中位数的像素值代替。
缺点:由于需要对周围的像素值进行排序所以需要的计算量比较大

lena_noise=cv2.imread("噪声lena.jpg",0)
lena_deal=cv2.medianBlur(lena_noise,5)
lena_noise = np.concatenate((np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2)),
                         axis = -1)
lena_deal = np.concatenate((np.expand_dims(lena_deal, axis = 2), np.expand_dims(lena_deal, axis = 2), np.expand_dims(lena_deal,axis = 2)),
                         axis = -1)
pylab.subplot(1,2,1)
pylab.imshow(lena_noise)
pylab.subplot(1,2,2)
pylab.imshow(lena_deal)
pylab.show()

5. 双边滤波
cv2.bilateralFilter (img,d, sigmaColor, sigmaSpace)
说明:
d 为邻域直径,sigmaColor 为空间高斯函数标准差,参数越大,临近像素将会在越远的地方越小。
sigmaSpace 灰度值相似性高斯函数标准差,参数越大,那些颜色足够相近的的颜色的影响越大。

双边滤波是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空间与信息和灰度相似性,达到保边去噪的目的。
具有简单、非迭代、局部处理的特点。
之所以能够达到保边去噪的滤波效果是因为滤波器由两个函数构成:
1. 一个函数是由几何空间距离决定滤波器系数
2. 另一个是由像素差值决定滤波器系数。
缺点:处理耗时。
优点:在滤波的同时能保证一定的边缘信息。

lena_noise=cv2.imread("噪声lena.jpg",0)
lena_deal=cv2.bilateralFilter(lena_noise,25,100,100)
lena_noise = np.concatenate((np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2)),
                         axis = -1)
lena_deal = np.concatenate((np.expand_dims(lena_deal, axis = 2), np.expand_dims(lena_deal, axis = 2), np.expand_dims(lena_deal,axis = 2)),
                         axis = -1)
pylab.subplot(1,2,1)
pylab.imshow(lena_noise)
pylab.subplot(1,2,2)
pylab.imshow(lena_deal)
pylab.show()

6.2D 卷积
dst=cv2.filter2D (src,ddepth,ksize,anchor,delta,borderType)

dst 是返回值
src 是需要处理的图像
ddepth 处理结果图像的深度,如果是 - 1 表示与原始图像使用相同的图片深度
kszie 是滤波核 (卷积核) 的大小
anchor 是锚点,默认值是(-1,-1)一般无需更改
delta 修正值,作为可选项。如果存在,会在结果上加上该值作为最终滤波的处理结果
borderType 是边界样式,一般无需更改
一般情况下,使用 dst=cv2.blur (src,ksize) 即可
原理:它只取内核区域下所有像素的平均值并替换中心元素。3x3 标准化的盒式过滤器如下所示:

lena_noise=cv2.imread("噪声lena.jpg",0)
kernel = np.ones((5,5),np.float32)/25
lena_deal = cv2.filter2D(lena_noise,-1,kernel)
lena_noise = np.concatenate((np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2), np.expand_dims(lena_noise, axis = 2)),
                         axis = -1)
lena_deal = np.concatenate((np.expand_dims(lena_deal, axis = 2), np.expand_dims(lena_deal, axis = 2), np.expand_dims(lena_deal,axis = 2)),
                         axis = -1)
pylab.subplot(1,2,1)
pylab.imshow(lena_noise)
pylab.subplot(1,2,2)
pylab.imshow(lena_deal)
pylab.show()

# C++ 部分

# 环境配置

# 配置

安装好 OpenCV 后,打开的 OpenCV 文件夹如下:

如果是 VS2015,则选择 vc14;如果是 VS2017 或者 19,则选择 vc15,选择 vc14 也没有问题。

配置成 release 版本的

右击 release,点击属性。

配置 VC++ 目录下的包含目录和库目录

D:\software\opencv\build\include\opencv2

D:\software\opencv\build\include

D:\software\opencv\build\x64\vc15\lib

在配置 链接器输入 中的 附加依赖项

后面加 d 的表示 debug 版本的,因为上面配置的是 release 版本的,所以选择不加 d 的。

配置环境变量,由于使用的是 vs2022,所以配置的是 vc15 的。

# 测试代码

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;


int main() {
	Mat img = imread("D:\\Temp\\images\\img.jpg");
	imshow("img", img);
	waitKey(0);
	destroyAllWindows();
}

更新于

请我喝[茶]~( ̄▽ ̄)~*

yuan 微信支付

微信支付

yuan 支付宝

支付宝

yuan 贝宝

贝宝