zl程序教程

您现在的位置是:首页 >  Python

当前栏目

Python数字图像处理与机器视觉

2023-04-18 14:38:48 时间

1. 彩色图像处理

1.1 图像读取

使用python PIL库读取图像,该方法返回一个 Image 对象,Image对象存储着这个图像的格式(jpeg,jpg,ppm等),大小和颜色模式(RGB),它含有一个show()方法用来显示图像:

# 导入PIL库
from PIL import Image
# 使用 Image 类读取图像
img = Image.open("Image-Progcess/image.png")
# 查看图像的信息
print(img.format,img.size,img.mode)
# 显示图像
img.show()

读取图像不需要制定它的格式。

1.2 写入图像

使用Image对象以不同的格式写入图像文件,需要指定它的格式:

# 写入图像
# 引入系统库,提供获取目录的方法
# 导入PIL库
from PIL import Image
import os,sys
​
# Image对象使用save方法存储图像文件
# 将文件转换为JPEG
# sys.argv[1:]是使用 python file.py [args]调用该python模块时的参数[args]
for infile in sys.argv[1:]:
    f,e = os.path.splitext(infile)
    outfile = f + ".png"
    print(outfile)
    if infile != outfile:
        try:
            img = Image.open("image.png")
            print(img.size)
            with Image.open(outfile) as im:
                print(im.size)
                im.save(f+'.jpg')
                im.save(f+'.ppm')
                im.save(f+'.bmp')
        except OSError as e:
            print('cannot convert',str(e))

1.3 (位图) BMP图像格式简介

BMP格式,也称为Bitmap (位图),是Windows系统中广泛使用的图像文件格式,由于它可以不做任何变换的保存图像像素域的数据,它成为了我们获取RAW数据的重要来源。

BMP文件的数据按照文件头开始的先后顺序分为四个部分:

  • bmp文件头:提供文件的格式,大小等信息
  • 位图信息头:提供图像数据的尺寸,位平面数,压缩方式,颜色索引等信息。
  • 调色板 (可选):这个部分可选,用索引表示颜色。

1.3 位图 (BMP) 的位数 (32位,16位)

位图是用一个位数组表示,32位和16位表示颜色质量,即每个像素用多少比特位表示 (1、4、8、15、24、32 或 64) ,这个数字在文件头中指定。

1.4 位图的色数 (256色,16色,单色)

位图的色数由调色板决定,只有4,8位图像才会使用调色板数据,16,24,32位图像不需要调色板数据,调色板最多只需要256项(索引0 - 255)。调色板的大小根据所使用的颜色模式而定:2色图像为8字节;16色图像位64字节;256色图像为1024字节。

1.5 图像的格式 (BMP,JPG,GIF,PNG)

类型

优点

缺点

应用场景

相同图片大小比较

BMP

无损保存,图质最好,广泛支持

体积太大,不利于保存和网络传输

57.1MB

GIF

动画存储格式

最多256色,画质差

PNG

几乎无损的压缩和较好的质量

质量较高

1.4MB

JPG

压缩率高,利于网络传输

质量一般

车牌识别

425KB

2. 机器视觉

2.1 奇异值分解 (SVD) 简介

奇异值分解是矩阵分解方法之一,它将矩阵分解为3个子矩阵,即U,S,V,其中U是左特征向量,S是奇异值的对角矩阵,V称为右特征向量。

使用numpy的 linalg.svd() 方法重建图像的SVD矩阵。

linalg.svd(矩阵,full_matrices=True,compute_uv=True,hermitian=False)

参数:

  1. matrix :大小 > 2 的实数或复数矩阵。
  2. full_matrices:如果为 True,则 u 和 v 矩阵的大小为 mxn ,如果为 False 则 u 和 v 矩阵的形状为 mxk ,其中 k 仅为非零值。
  3. compute_uv:采用布尔值来计算 u 和 v 矩阵以及 s 矩阵。
  4. hermitian:默认情况下,如果矩阵包含实数值,则假定矩阵是 Hermitian,这在内部用于有效计算奇异值。

2.2 奇异值分解 (SVD) 对图片进行降维处理

2.2.1 读取图像

使用linalg.svd()方法分解矩阵,查看图像具有多少个线性无关的特征向量。

# 导入模块
import requests
import cv2
import numpy as np
import matplotlib.pyplot as plt
​
# 分配并打开图像
url = 'https://media.geeksforgeeks.org/wp-content/cdn-uploads/20210401173418/Webp-compressed.jpg'
response = requests.get(url, stream=True)
​
with open('image.png', 'wb') as f:
    f.write(response.content)
​
img = cv2.imread('image.png')
​
# 将图像转换为灰度以加快速度
# 计算。
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
​
# 计算SVD
u, s, v = np.linalg.svd(gray_image, full_matrices=False)
​
# 检查矩阵的形状
print(f'u.shape:{u.shape},s.shape:{s.shape},v.shape:{v.shape}')

输出形状表明该图像具有3648个线性无关的特征向量。

2.2.2 查看在奇异向量中使用的图像的方差
# 导入模块
import seaborn as sns
 
var_explained = np.round(s**2/np.sum(s**2), decimals=6)
 
# 方差解释顶部奇异向量
print(f'variance Explained by Top 20 singular values:
{var_explained[0:20]}')
 
sns.barplot(x=list(range(1, 21)),
            y=var_explained[0:20], color="dodgerblue")
 
plt.title('Variance Explained Graph')
plt.xlabel('Singular Vector', fontsize=16)
plt.ylabel('Variance Explained', fontsize=16)
plt.tight_layout()
plt.show()

上面的方差解释图表明,大约99.77%的信息是由第一个特征向量及其相应的特征值本身解释的。

2.2.3 仅使用前几个特征向量来重建图像

使用不同个数的奇异值来重组图像:

# 用不同数量的奇异值绘制图像
comps = [3648, 1, 5, 10, 15, 20]
plt.figure(figsize=(12, 6))
 
for i in range(len(comps)):
    low_rank = u[:, :comps[i]] @ np.diag(s[:comps[i]]) @ v[:comps[i], :]
     
    if(i == 0):
        plt.subplot(2, 3, i+1),
        plt.imshow(low_rank, cmap='gray'),
        plt.title(f'Actual Image with n_components = {comps[i]}')
     
    else:
        plt.subplot(2, 3, i+1),
        plt.imshow(low_rank, cmap='gray'),
        plt.title(f'n_components = {comps[i]}')

2.3 图像的开闭运算 (腐蚀-膨胀)

2.3.1 腐蚀

侵蚀的基本思想就像土壤侵蚀一样,它侵蚀了前景对象的边界(始终尽量保持前景为白色)。那么它有什么作用呢?内核在图像中滑动(如在 2D 卷积中)。仅当内核下的所有像素都为 1 时,原始图像中的像素(1 或 0)才会被视为 1,否则它会被侵蚀(使其为零)。

腐蚀操作是将结构元素覆盖下的中心像素点的值用最小值替换 :

import cv2
import numpy as np
​
img = cv2.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(img,kernel,iterations = 1)
2.3.2 膨胀

膨胀操作是将结构元素覆盖下的中心像素点的值用最大值替换:

dilation = cv2.dilate(img,kernel,iterations = 1)
2.3.3 霍夫圆变换—cv2.HoughCircles

HoughCircles() 函数仅接受单通道图像,在使用图像之前要先使用 cv2.cvtColor() 函数获取灰度图像:

# 灰色图像
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

使用霍夫圆变换的画圆函数:

# 画圆
def drawCircle(image):
    # 霍夫圆变换
    circles = cv2.HoughCircles(
    image,
    cv2.HOUGH_GRADIENT,
    1,
    20,
    param1=50,
    param2=30,
    minRadius=0,
    maxRadius=0
    )
    
    # 确保至少找到了一些圆圈
    output = image.copy()
    if circles is not None:
        # 将圆的 (x, y) 坐标和半径转换为整数
        circles = np.round(circles[0, :]).astype("int")
        # 循环 (x, y) 坐标和圆的半径
        for (x, y, r) in circles:
            # 在输出图像中绘制圆形,然后绘制矩形
            # 对应圆心
            cv2.circle(output, (x, y), r, (0, 255, 0), 4)
        # 显示输出图像
        cv2.imshow("output", np.hstack([image, output]))
        cv2.waitKey(0)

1) 使用模糊灰度图像和霍夫圆变换

img = cv2.imread('Image-Progcess/image1.png')
# 灰色图像
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 模糊灰度图像 
blurred = cv2.medianBlur(gray,5)
# 画圆
drawCircle(blurred)

2) 使用腐蚀运算和霍夫圆变换

erosion = cv2.erode(img,kernel,iterations = 1)

3) 使用膨胀运算

dilation = cv2.dilate(img,kernel,iterations = 1)

2.4 图像梯度,开闭,轮廓运算

2.4.1 开闭运算,形态梯度

开操作是首先对图像进行腐蚀操作,然后在对图像进行膨胀操作,开操作可以断开两个对象的连接性。实现对象分离。

闭操作使用结构元素对图像先膨胀后腐蚀,正好跟开操作的顺序相反,但是闭操作绝对不是开操作的反操作结果。

形态学的梯度操作是图像膨胀与腐蚀结果之间的不同 :

# 开运算
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
# 闭运算
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
#形态梯度
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
2.4.2 使用边缘检测物体轮廓

为了检测圆形或任何其他几何形状,我们首先需要检测图像中存在的对象的边缘。

图像中的边缘是颜色急剧变化的点。例如,白底红球的边缘是一个圆。为了识别图像的边缘,一种常用的方法是计算图像梯度。

找到图像的轮廓:

cnts = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
2.4.3 条形码检测
# 导入所需库
import numpy as np
import imutils
import cv2
# 转换为灰度图像
image = cv2.imread('Image-Progcess/tiaoxingma2.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 使用Scharr算子的边缘检测方法
ddepth = cv2.CV_32F if imutils.is_cv2() else cv2.CV_32F
gradX = cv2.Sobel(gray, ddepth=ddepth, dx=1, dy=0, ksize=-1)
gradY = cv2.Sobel(gray, ddepth=ddepth, dx=0, dy=1, ksize=-1)
gradient = cv2.subtract(gradX,gradY)
gradient = cv2.convertScaleAbs(gradient)
#去除噪声
## 模糊和阈值化处理
blurred = cv2.blur(gradient,(9, 9))
(_, thresh) = cv2.threshold(blurred, 231, 255, cv2.THRESH_BINARY)
## 形态学处理
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
closed = cv2.erode(closed, None, iterations=4)
closed = cv2.dilate(closed, None, iterations=4)
# 确定检测轮廓,画出检测框
cnts = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
 
rect = cv2.minAreaRect(c)
box = cv2.boxPoints(rect) if imutils.is_cv2() else cv2.boxPoints(rect)
box = np.int0(box)
 
cv2.drawContours(image, [box], -1, (0, 255, 0), 3)
cv2.imshow("Image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

3. 参考

python+OpenCV检测条形码

OpenCV中图像形态学操作