如何使用Python摆脱模糊的照片

当我们拍摄大量照片时,其中一些是模糊的。一家大型汽车公司也面临同样的问题。在检查汽车时,有些照片模糊不清,可能会对销量产生负面影响。

低质量的图像会直接降低利润。


  • 应用程序如何在算法级别识别模糊照片?
  • 如何测量RGB图像的清晰度?



问题的提法


我在一家大型汽车公司担任分析师。在检查汽车时,在检查汽车时,他们会通过特殊的应用程序拍摄很多照片,这些照片会立即发送到数据库中。有些图像模糊不清,不利于销售。

由此产生的问题是:“如何在算法级别识别模糊图片?”

根据1200张汽车不同元素的照片样本,开发了一种算法。该示例的一个特征是未标记,因为 很难确定哪些图片清晰,哪些图片清晰。

事实证明,“与老师一起”学习机器学习模型不适用于该解决方案。

在工作过程中,我使用了以下工具:

  • Python库:numpy,matplotlib,cv2;
  • Jupyter笔记本。

在本文中,我将描述我所遇到的问题的解决方案。

解决问题的方法的描述


阶段1.定义边界


什么照片可以称为清晰?
一种对象的边界是明确的。在模糊拍摄中,物体的边界会模糊。

如何确定图片中对象的边界?

我们看到最大的色差的边框。

事实证明,要确定图像的清晰度,首先需要确定照片对象的边界,然后评估它们的大小,厚度,数量等。

这张照片由3到3个数字组成,从0到255 :(宽度,高度,3种颜色)。
我通过像创建深度神经网络一样应用过滤器来定义边界:通过将三维数组乘以矩阵(每种颜色):

    │ 1 -1 │
    │ 1 -1 │

具有色差,所得阵列将产生高模数。
因此,我们定义了垂直和水平边界。算术平均值显示照片的公共边界。

第二阶段。边界分析


边界已定义。

如何区分模糊图像的边缘与清晰边缘?

通过不同的选择,我发现了以下方法:

  1. 定义原始照片的边界(在步骤1中描述);
  2. 使原始图像模糊;
  3. 定义模糊图像的边界(在步骤1中描述);
  4. 我们考虑第一段和第二段的算术平均值之比;
  5. 所得系数表征了图像的清晰度。

逻辑很简单:在清晰的照片中,边界的变化将比在模糊的情况中发生得更明显,这意味着系数将更高。

该算法的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下,我们指定要包括在边界估计中的像素数。矩阵的方向可以是水平或垂直的。

进一步的功能类似于深度神经网络层:

# Apply one filter defined by parameters W and single slice
def conv_single_step(a_slice_prev, W):
    s = W * a_slice_prev
    Z = np.sum(s)
    Z = np.abs(Z)
    
    return Z
   
# Full edge filter
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

# pooling
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), 
    :]

为了进行分析,我们不使用整个图像,而仅使用其中心部分,因为 相机会更多地聚焦在中央。如果图片清晰,则中心将清晰。

以下功能使用以前的功能确定图像中对象的边界:

# main layer
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)))

该函数确定垂直边界,然后确定水平边界,并返回两个数组的算术平均值。

以及发出定义参数的主要功能:

# calculate borders of original and blurred images
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帐户上找到有效的算法

All Articles