当我们拍摄大量照片时,其中一些是模糊的。一家大型汽车公司也面临同样的问题。在检查汽车时,有些照片模糊不清,可能会对销量产生负面影响。
低质量的图像会直接降低利润。- 应用程序如何在算法级别识别模糊照片?
- 如何测量RGB图像的清晰度?

问题的提法
我在一家大型汽车公司担任分析师。在检查汽车时,在检查汽车时,他们会通过特殊的应用程序拍摄很多照片,这些照片会立即发送到数据库中。有些图像模糊不清,不利于销售。由此产生的问题是:“如何在算法级别识别模糊图片?”根据1200张汽车不同元素的照片样本,开发了一种算法。该示例的一个特征是未标记,因为 很难确定哪些图片清晰,哪些图片清晰。事实证明,“与老师一起”学习机器学习模型不适用于该解决方案。在工作过程中,我使用了以下工具:- Python。库:numpy,matplotlib,cv2;
- Jupyter笔记本。
在本文中,我将描述我所遇到的问题的解决方案。解决问题的方法的描述
阶段1.定义边界
什么照片可以称为清晰?一种对象的边界是明确的。在模糊拍摄中,物体的边界会模糊。如何确定图片中对象的边界?我们看到最大的色差的边框。事实证明,要确定图像的清晰度,首先需要确定照片对象的边界,然后评估它们的大小,厚度,数量等。这张照片由3到3个数字组成,从0到255 :(宽度,高度,3种颜色)。我通过像创建深度神经网络一样应用过滤器来定义边界:通过将三维数组乘以矩阵(每种颜色): │ 1 -1 │
│ 1 -1 │
具有色差,所得阵列将产生高模数。因此,我们定义了垂直和水平边界。算术平均值显示照片的公共边界。第二阶段。边界分析
边界已定义。如何区分模糊图像的边缘与清晰边缘?通过不同的选择,我发现了以下方法:- 定义原始照片的边界(在步骤1中描述);
- 使原始图像模糊;
- 定义模糊图像的边界(在步骤1中描述);
- 我们考虑第一段和第二段的算术平均值之比;
- 所得系数表征了图像的清晰度。
逻辑很简单:在清晰的照片中,边界的变化将比在模糊的情况中发生得更明显,这意味着系数将更高。该算法的Python实现
为了直接解决问题,我们使用以下库:import numpy as np
import matplotlib.pyplot as plt
import cv2
对于确定边界的参数,我们定义矩阵定义函数:def edges(n, orient):
edges = np.ones((2*n, 2*n, 3))
if orient == 'vert':
for i in range(0, 2*n):
edges[i][n: 2*n] *= -1
elif orient == 'horiz':
edges[n: 2*n] *= -1
return edges
在参数n下,我们指定要包括在边界估计中的像素数。矩阵的方向可以是水平或垂直的。进一步的功能类似于深度神经网络层:
def conv_single_step(a_slice_prev, W):
s = W * a_slice_prev
Z = np.sum(s)
Z = np.abs(Z)
return Z
def conv_forward(A_prev, W, hparameters):
m = len(A_prev)
(f, f, n_C) = W.shape
stride = hparameters['stride']
pad = hparameters['pad']
Z = list()
flag = 0
z_max = hparameters['z_max']
if len(z_max) == 0:
z_max = list()
flag = 1
for i in range(m):
(x0, x1, x2) = A_prev[i].shape
A_prev_pad = A_prev[i][
int(x0 / 4) : int(x0 * 3 / 4),
int(x1 / 4) : int(x1 * 3 / 4),
:]
(n_H_prev, n_W_prev, n_C_prev) = A_prev_pad.shape
n_H = int((n_H_prev - f + 2*pad) / stride) + 1
n_W = int((n_W_prev - f + 2*pad) / stride) + 1
z = np.zeros((n_H, n_W))
a_prev_pad = A_prev_pad
for h in range(n_H):
vert_start = h * stride
vert_end = h * stride + f
for w in range(n_W):
horiz_start = w * stride
horiz_end = w * stride + f
a_slice_prev = a_prev_pad[vert_start: vert_end, horiz_start: horiz_end, :]
weights = W[:, :, :]
z[h, w] = conv_single_step(a_slice_prev, weights)
if flag == 1:
z_max.append(np.max(z))
Z.append(z / z_max[i])
cache = (A_prev, W, hparameters)
return Z, z_max, cache
def pool_forward(A_prev, hparameters, mode = 'max'):
m = len(A_prev)
f = hparameters['f']
stride = hparameters['stride']
A = list()
for i in range(m):
(n_H_prev, n_W_prev) = A_prev[i].shape
n_H = int(1 + (n_H_prev - f) / stride)
n_W = int(1 + (n_W_prev - f) / stride)
a = np.zeros((n_H, n_W))
for h in range(n_H):
vert_start = h * stride
vert_end = h * stride + f
for w in range(n_W):
horiz_start = w * stride
horiz_end = w * stride + f
a_prev_slice = A_prev[i][vert_start: vert_end, horiz_start: horiz_end]
if mode == 'max':
a[h, w] = np.max(a_prev_slice)
elif mode == 'avg':
a[h, w] = np.mean(a_prev_slice)
A.append(a)
cache = (A_prev, hparameters)
return A, cache
conv_single_step-图像颜色乘以显示边界的矩阵的乘积。conv_forward-整个照片边框的完整定义。pool_forward-减小结果数组的大小。另外,我注意到conv_forward函数中各行的值:(x0, x1, x2) = A_prev[i].shape
A_prev_pad = A_prev[i][
int(x0 / 4) : int(x0 * 3 / 4),
int(x1 / 4) : int(x1 * 3 / 4),
:]
为了进行分析,我们不使用整个图像,而仅使用其中心部分,因为 相机会更多地聚焦在中央。如果图片清晰,则中心将清晰。以下功能使用以前的功能确定图像中对象的边界:
def borders(images, filter_size = 1, stride = 1, pool_stride = 2, pool_size = 2, z_max = []):
Wv = edges(filter_size, 'vert')
hparameters = {'pad': pad, 'stride': stride, 'pool_stride': pool_stride, 'f': pool_size, 'z_max': z_max}
Z, z_max_v, _ = conv_forward(images, Wv, hparameters)
print('edge filter applied')
hparameters_pool = {'stride': pool_stride, 'f': pool_size}
Av, _ = pool_forward(Z, hparameters_pool, mode = 'max')
print('vertical filter applied')
Wh = edges(filter_size, 'horiz')
hparameters = {'pad': pad, 'stride': stride, 'pool_stride': pool_stride, 'f': pool_size, 'z_max': z_max}
Z, z_max_h, _ = conv_forward(images, Wh, hparameters)
print('edge filter applied')
hparameters_pool = {'stride': pool_stride, 'f': pool_size}
Ah, _ = pool_forward(Z, hparameters_pool, mode = 'max')
print('horizontal filter applied')
return [(Av[i] + Ah[i]) / 2 for i in range(len(Av))], list(map(np.max, zip(z_max_v, z_max_h)))
该函数确定垂直边界,然后确定水平边界,并返回两个数组的算术平均值。以及发出定义参数的主要功能:
def orig_blur(images, filter_size = 1, stride = 3, pool_stride = 2, pool_size = 2, blur = 57):
z_max = []
img, z_max = borders(images,
filter_size = filter_size,
stride = stride,
pool_stride = pool_stride,
pool_size = pool_size
)
print('original image borders is calculated')
blurred_img = [cv2.GaussianBlur(x, (blur, blur), 0) for x in images]
print('images blurred')
blurred, z_max = borders(blurred_img,
filter_size = filter_size,
stride = stride,
pool_stride = pool_stride,
pool_size = pool_size,
z_max = z_max
)
print('blurred image borders is calculated')
return [np.mean(orig) / np.mean(blurred) for (orig, blurred) in zip(img, blurred)], img, blurred
首先,我们确定原始图像的边界,然后对图像进行模糊处理,然后确定模糊照片的边界,最后,我们考虑原始图像和模糊图像的算术平均边界之比。该函数返回定义因子列表,原始图像的边界数组和模糊的边界数组。算法运算示例
为了进行分析,我从freepik.com图片库中拍摄了照片。


我们确定模糊前后的第一幅图像的边界:
第二:
第三:
第四:
在图像中,可以看到清晰图片(第三和第四)的边界变化比模糊图片(第一和第二)的边界变化更强。计算之后,我们得到的系数:[5.92918651681958,2.672756123184502,10.695051017699232,11.901115749698139]的系数确认结论:较大的系数,越清晰的照片。此外,第二张图片不如第一张清晰,这反映在系数中。方法特点
- 图片越清晰,边框变化越强,这意味着参数越高;
- 对于不同的需求,需要不同的清晰度。因此,有必要自行确定清晰度的界限:在某处,足够清晰的照片的系数将在7以上,而在10以上才可以。
- 系数取决于照片的亮度。深色照片的边界将变弱,这意味着系数将变小。事实证明,必须在考虑照明的情况下(即对于标准照片)确定清晰度的界限;
可以在我的github帐户上找到有效的算法。