欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

基于opencv视觉库,通过numpy进行像素矩阵处理,压缩图片、做像素图

时间:2023-04-29
1、numpy库基本介绍

NumPy 是一个Python包(Numeric Python)它是一个由多维数组对象和用于处理数组的例程集合组成的库,其支持大量高维度数组与矩阵运算。NumPy 也针对数组运算提供大量的数学函数,机器学习涉及到大量对数组的变换和运算。

导入Numpy库,并命名为 np,起别名简化代码书写

import numpy as np

NumPy 中定义的最重要的对象是称为 ndarray 的 N 维数组类型。 它描述相同类型的元素集合。可以使用基于零的索引访问集合中的项目。

ndarray 中的每个元素在内存中使用相同大小的块, ndarray 中的每个元素是数据类型对象的对象(称为 dtype)

基本的ndarray是使用NumPy中的数组函数创建的,如下所示:

ndarray_obj = numpy.array(*args, **kwargs)

NumPy 的主要对象是多维数组 Ndarray。在 NumPy 中维度 Dimensions 叫做轴 Axes,轴的个数叫做秩 Rank。注意,numpy.array 和 Python 标准库 array.array 并不相同,前者更为强大,这也就是我们学习 NumPy 的重要原因。

2、numpy库常见函数使用

其实numpy库中的相关函数和Matlab中的相关方法非常相似,学过Matlab的同学可以很容易去了解使用numpy的函数,源码注释也清楚展示了。

平时使用最多来生成多维数组的就是 numpy.array,它从任何暴露数组接口的对象,或从返回数组的任何方法创建一个 ndarray 对象。

numpy.array(object, dtype = None, copy = True, order = None, subok = False, ndmin = 0)

numpy.array 构造器接受的参数具体展示:

举例测试,通过 numpy.array 生成多维数组,dtype=complex 是生成复数形式的多维数组。

import numpy as npmy_array = np.array([1, 2, 3], dtype=complex)print(my_array)# [1.+0.j 2.+0.j 3.+0.j]

ndarray.shape

这一数组属性返回一个包含数组维度的元组,它也可以用于调整数组大小。

import numpy as np a = np.array([[1,2,3],[4,5,6]]) print a.shape # ( 2,3 )

3、numpy库数据类型

NumPy 支持比 Python 更多种类的数值类型, 下表显示了 NumPy 中定义的不同标量数据类型。

序号数据类型描述1.bool_存储为一个字节的布尔值(真或假)2.int_默认整数,相当于 C 的long,通常为int32或int643.intc相当于 C 的int,通常为int32或int644.intp用于索引的整数,相当于 C 的size_t,通常为int32或int645.int8字节(-128 ~ 127)6.int1616 位整数(-32768 ~ 32767)7.int3232 位整数(-2147483648 ~ 2147483647)8.int6464 位整数(-9223372036854775808 ~ 9223372036854775807)9.uint88 位无符号整数(0 ~ 255)10.uint1616 位无符号整数(0 ~ 65535)11.uint3232 位无符号整数(0 ~ 4294967295)12.uint6464 位无符号整数(0 ~ 18446744073709551615)13.float_float64的简写14.float16半精度浮点:符号位,5 位指数,10 位尾数15.float32单精度浮点:符号位,8 位指数,23 位尾数16.float64双精度浮点:符号位,11 位指数,52 位尾数17.complex_complex128的简写18.complex64复数,由两个 32 位浮点表示(实部和虚部)19.complex128复数,由两个 64 位浮点表示(实部和虚部)

NumPy 数字类型是 dtype(数据类型)对象的实例,每个对象具有唯一的特征,这些类型可以是np.bool_,np.float32等。

数据类型对象 (dtype) 描述了对应于数组的固定内存块的解释,取决于以下方面:

数据类型(整数、浮点或者 Python 对象)

数据大小(内存)

数据字节序(小端:最小有效字节存储在最小地址中、大端:最大有效字节存储在最小地址中)

在结构化类型的情况下,字段的名称,每个字段的数据类型,和每个字段占用的内存块部分。

如果数据类型是子序列,它的形状和数据类型。

数据类型对象 (dtype) 可由一下语法构造:

numpy.dtype(object, align, copy)

object:被转换为数据类型的对象。

align:如果为true,则向字段添加间隔,使其类似 C 的结构体。

copy:生成dtype对象的新副本,如果为flase,结果是内建数据类型对象的引用。

4、opencv读取图片原理

首先我们测试如何使用opencv查看一张图片,我们下载的是opencv视觉库,但是引入的时候是引入cv2,调用 imshow函数进行开启窗口查看图片。

import cv2image = cv2.imread("image/test.jpeg")cv2.imshow("window", image)

运行结果:窗口一闪而过

因为程序一旦停止运行,图片就不会展示了,所以会出现一闪而过的窗口展示,所以为了让图片长时间展示出来,那么需要加:cv2.waitKey()

图片像素矩阵概念

数字图像数据可以用矩阵来表示,因此可以采用矩阵理论和矩阵算法对数字图像进行分析和处理。由于数字图像可以表示为矩阵的形式,所以在计算机数字图像处理程序中,通常用二维数组来存放图像数据。

cv库中的函数cv.image读取的是图片的像素矩阵,矩阵单元是rbg的向量形式。下面举例读取纯色图片来解释原理情况:

import cv2image = cv2.imread("image/test_98_98.jpeg")# 返回矩阵 矩阵的每个是rgb行向量,[r,g, b]""" · The function imread loads an image from the specified file and returns it、If the image cannot be 、 read (because of missing file, improper permissions, unsupported or invalid format), the function 、 returns an empty matrix ( Mat::data==NULL )."""print(len(image)) # 像素:高print(len(image[0])) # 像素:宽print(image) # 像素矩阵(3维列表)cv2.imshow("window", image)cv2.waitKey(0)

三维列表:最外维是高,中间维度是宽,最里面的维度是rgb

就比如读取一张纯色(40,44,52)的jpeg图片,发现矩阵的每个是rgb行向量都是相同的。

jpeg与png区别:png可以存储透明的图片,因为png图片的像素单元,是4个数值的元组进行存储,分别对应 Red、Green、Blue、透明度

而我们采用传统的文件读取方式,读出结果都是二进制的格式:

with open('./image/test_98_98.png', 'rb') as f: print(f.read())

5、池化图像RGB矩阵

最常见的池化操作为平均池化mean pooling和最大池化max pooling:

平均池化:计算图像区域的平均值作为该区域池化后的值。

最大池化:选图像区域的最大值作为该区域池化后的值。

随机池化:只需对feature map中的元素按照其概率值大小随机选择,即元素值大的被选中的概率也大。

在提取信息的时候,在池化的时候,如果取区域均值(mean-pooling),往往能保留整体数据的特征,能凸出背景的信息,而如果取区域最大值(max-pooling),则能更好保留纹理上的特征,但这些应该都不如小波变换那样,可以保留更多的细节特征,整体上也应该更加细微。

特别注意:池化图像RGB矩阵,可能有同学没有学过神经网络卷积与池化方面的知识,这边做个简单的替代操作,把多组像素点,合并成某理想化的像素点。

将图片进行预处理。因为这里我们进行池化操作的过滤器的尺寸是 10 * 10,所以对与一些图片尺寸的大小(长宽)不能为10整除的,我们需要进行处理。

采用均值池化的代码展示如下所示:

import numpy as npfrom PIL import Imageimport matplotlib.pyplot as plt# 均值池化def Avgpooling(data, m, n): rows, cols = data.shape newImage = [] for i in range(0, rows, m): line = [] # 记录每一行 for j in range(0, cols, n): res = data[i:i + m, j:j + n] # 选取池化区域 line.append(np.sum(res) / (n * m)) newImage.append(line) return np.array(newImage)img = Image.open('mini_tang.jpeg') # 打开图片r, g, b = img.split() # 分割像素 r / g / bplt.imshow(img); plt.axis('off'); plt.show(); # 展示图片img_new = Avgpooling(np.array(r), 10, 10)plt.imshow(img_new); plt.axis('off'); plt.show(); # 展示图片

均值池化如下所示:左边是原图片,右边是均值池化后的图片(像素图)

如果采用的是最大池化的方式,具体代码如下所示:

# 最大池化def Maxpooling(data, m, n): rows, cols = data.shape newImage = [] for i in range(0, rows, m): line = [] for j in range(0, cols, n): res = data[i:i + m, j:j + n] line.append(np.max(res)) newImage.append(line) return np.array(newImage)

最大池化如下所示:左边是原图片,右边是均值池化后的图片(像素图)

6、ndarray对象迭代注意点

今天遇到了一个问题,也继续展示和大家聊下,对于一个三维数组ndarray对象,我通过迭代的方式(三层迭代)去进行重新赋值,发现失败了。

for row in ndarray_zeros: #(100,100,3) 三维 0 数组 for col in row: for item in col: item = 5.20 print(item) # 0.0

运行结果是:可以打印item的值,都是0.0,说明赋值失败了,到底是怎么回事呢?

我刚开始以为,会不会浅拷贝与深拷贝的问题,其实不然,请看我下面的叙述。

说到迭代,大家马上想到的大概是直接用个for循环简单粗暴地就对这个数组进行迭代,但是for循环只能对第一个axis迭代,也就是axis=0,除非一维数组的迭代,大部分情况下都不会用到for循环对整个ndarray数组迭代。

用nditer进行迭代,实现对ndarray多维数组的迭代

推荐用nditer进行迭代,因为nditer采用的是底层的C语言或者Fortran循环,比Python中的循环快。

arr = numpy.arange(6).reshape(2, 3)for item in numpy.nditer(arr): print(item, end=' ')# 0 1 2 3 4 5

在对nditer函数不传入任何可选参数的情况下,默认模式为只读模式,即无法对原数组进行更改,要更改原来的数组的值,要对默认参数进行设置。

for i in np.nditer(a,op_flags=['readwrite']):

补充下:ndarray的复制的注意

对于ndarray,需要特别小心的是,当把一个多维数组复制给另一个变量的时候,其实这两个变量是相同的,对其中一个变量指向的多维数组进行修改也会同时对另外一个变量的指向的多维数组的值进行修改!

要真正把数组复制给另外一个变量,得用copy的方法:

arr = np.arange(6).reshape(2,3)new_arr = arr.copy()

7、opencv压缩图片

首先,当我们opencv.imread()某张图片的时候,获取的对象是rgb的三维矩阵,对象类型是numpy.ndarray类型,其实就是三维的数组,分别行、列、rbg单位

ndarray对象都有一个属性是shape,返回当前多维数组的尺寸:

比如,我们打开一张300*300像素的jpeg图片,如下image变量就是numpy.ndarray类型的对象,我们打印它的尺寸就是:(300, 300, 3)

image = cv2.imread("mini_tang.jpeg")print(image.shape) # (300, 300, 3)

上面我们讲的池化处理的方式,现在我们来讲如何压缩图片,简单来讲,比如:其实就是把5 * 5像素组取一个理想的像素点(从而代替5 * 5像素组),实现压缩图片处理。

为了实现压缩图片的处理,代码如下所示:

import cv2import numpyndarray_zeros = numpy.zeros((100, 100, 3))def SmallerImage(arr, size): rows, cols, lens = arr.shape new_Image = [] middle_len = int((size + 1) / 2) # 获取中心索引点的值 for i in range(0, rows, size): # 超出纵向边界直接跳过 if i + middle_len >= rows: continue new_Image_line = [] # 记录每一行 for j in range(0, cols, size): # 超出横向边界直接跳过 if j + middle_len >= cols: continue new_Image_line_rgb = arr[i + middle_len][j + middle_len] # 选取中心像素点 new_Image_line.append(new_Image_line_rgb) new_Image.append(new_Image_line) # 添加line行 return numpy.array(new_Image)image = cv2.imread("son_weather.jpeg")cv2.imshow("old_image", image)cv2.imshow("new_image", SmallerImage(image,2))cv2.waitKey(0)

将上述的代码运行后的结果,右边是原图片,左边是压缩后的图片展示:

8、opencv制作像素图

像素图的制作方案,怎么来讲呢?其实实现起来是非常容易的,那么比如:其实就是把5 * 5像素组取一个理想的像素点,将 5 * 5像素组 的像素值全部转换成这个理想像素点的数值,一般来说都是取中心点。

为了实现像素图制作的处理,代码展示如下所示:

import cv2import numpyndarray_zeros = numpy.zeros((100, 100, 3))def XiangSuImage(arr, size): rows, cols, lens = arr.shape new_Image = [] middle_len = int((size + 1) / 2) # 获取中心索引点的值 for i in range(0, rows, size): new_Image_line = [] # 记录每一行 for j in range(0, cols, size): # 超出边界直接跳过 new_Image_line_rgb = [] if i + middle_len >= rows or j + middle_len >= cols: continue new_Image_line_rgb = arr[i + middle_len][j + middle_len] # 选取中心像素点 for k in range(size): new_Image_line.append(new_Image_line_rgb) for k in range(size): new_Image.append(new_Image_line) # 添加line行 return numpy.array(new_Image)image = cv2.imread("son_weather.jpeg")cv2.imshow("old_image", image)cv2.imshow("new_image", XiangSuImage(image,10))cv2.waitKey(0)

将上述的代码运行后的结果,右边是原图片,左边是像素图的展示:

喜欢的大佬们,小弟向您求个赞,阿爸阿爸!

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。