Cara menghilangkan foto buram menggunakan Python

Saat kami mengambil serangkaian bidikan besar, beberapa di antaranya tidak jelas. Sebuah perusahaan mobil besar menghadapi masalah yang sama. Beberapa foto selama inspeksi mobil ternyata buram, yang bisa berdampak negatif pada penjualan.

Gambar berkualitas rendah secara langsung mengurangi keuntungan.


  • Bagaimana cara aplikasi mengenali foto fuzzy pada level algoritma?
  • Bagaimana mengukur kejernihan gambar RGB?



Perumusan masalah


Saya bekerja sebagai analis di perusahaan mobil besar. Ketika memeriksa mobil, ketika memeriksa mobil, mereka mengambil banyak foto melalui aplikasi khusus, yang segera dikirim ke database. Beberapa gambar buram, yang buruk untuk penjualan.

Dari sini muncul masalah: "bagaimana cara mengenali gambar fuzzy pada level algoritma?"

Mengembangkan algoritme berdasarkan sampel 1200 foto berbagai elemen mobil. Fitur dari sampel adalah bahwa itu tidak diberi label, karena sulit untuk menentukan gambar mana yang jelas dan mana yang tidak.

Ternyata mempelajari model ML "dengan guru" tidak berlaku untuk solusi.

Selama bekerja saya menggunakan alat:

  • Python . Perpustakaan: numpy, matplotlib, cv2;
  • Notebook Jupyter .

Dalam artikel ini saya akan menjelaskan solusi untuk masalah yang saya datangi.

Deskripsi pendekatan untuk memecahkan masalah


Tahap 1. Menentukan batas


Foto apa yang bisa disebut jelas?
Satu di mana batas-batas objek diucapkan. Dalam bidikan fuzzy, batas objek kabur.

Bagaimana cara menentukan batas objek dalam gambar?

Berbatasan dengan tempat kita melihat perbedaan warna terbesar.

Ternyata untuk menentukan kejelasan gambar, pertama Anda perlu menentukan batas-batas objek foto, kemudian mengevaluasi ukuran, ketebalan, jumlah, dll.

Foto terdiri dari susunan angka tiga dimensi dari 0 hingga 255: (lebar, tinggi, 3 warna).
Saya mendefinisikan batas-batas dengan menerapkan filter seperti dalam menciptakan jaringan saraf yang dalam: dengan mengalikan array tiga dimensi dengan matriks (untuk setiap warna):

    │ 1 -1 │
    │ 1 -1 │

Dengan perbedaan warna, array yang dihasilkan akan menghasilkan angka modulus tinggi.
Jadi kita mendefinisikan batas vertikal dan horizontal. Rata-rata aritmatika menunjukkan batas umum foto.

Tahap 2. Analisis batas untuk kejelasan


Batas-batasnya ditentukan.

Bagaimana cara membedakan batas gambar fuzzy dari batas yang jelas?

Menelusuri berbagai opsi, saya menemukan pendekatan berikut:

  1. menentukan batas-batas foto asli (dijelaskan pada langkah 1);
  2. mengaburkan gambar asli;
  3. mendefinisikan batas-batas gambar buram (dijelaskan dalam langkah 1);
  4. kami mempertimbangkan rasio rata-rata aritmatika paragraf 1 dan paragraf 2;
  5. koefisien yang dihasilkan mencirikan kejelasan gambar.

Logikanya sederhana: dalam foto yang jelas, perubahan batas akan terjadi lebih signifikan daripada yang kabur, yang berarti bahwa koefisien akan lebih tinggi.

Implementasi algoritma python


Untuk memecahkan masalah secara langsung, kami menggunakan pustaka berikut:

import numpy as np
import matplotlib.pyplot as plt
import cv2

Untuk parameter untuk menentukan batas, kami mendefinisikan fungsi definisi matriks:

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

Di bawah parameter n, kami menentukan jumlah piksel yang kami sertakan dalam perkiraan batas. Orientasi matriks dapat horisontal atau vertikal.

Fungsi lebih lanjut mirip dengan lapisan jaringan saraf yang dalam:

# 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 - satu perkalian dari warna gambar dengan matriks mengungkapkan perbatasan.
conv_forward - Definisi lengkap perbatasan di seluruh foto.
pool_forward - kurangi ukuran array yang dihasilkan.

Secara terpisah, saya perhatikan nilai baris dalam fungsi 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), 
    :]

Untuk analisis, kami tidak menggunakan keseluruhan gambar, tetapi hanya bagian utamanya, karena kamera lebih sering fokus di tengah. Jika gambarnya jelas, maka bagian tengahnya akan jelas.

Fungsi berikut menentukan batas objek dalam gambar menggunakan fungsi sebelumnya:

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

Fungsi menentukan batas vertikal, lalu yang horisontal, dan mengembalikan rata-rata aritmatika dari kedua array.

Dan fungsi utama untuk mengeluarkan parameter definisi:

# 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

Pertama, kita menentukan batas-batas gambar asli, kemudian mengaburkan gambar, lalu kita menentukan batas-batas foto buram, dan akhirnya, kita mempertimbangkan rasio batas rata-rata aritmatika dari gambar asli dan kabur.

Fungsi mengembalikan daftar faktor definisi, array batas gambar asli dan array batas kabur.

Contoh Pengoperasian Algoritma


Untuk analisis, saya mengambil gambar dari stok foto freepik.com.









Kami menentukan batas gambar pertama sebelum dan sesudah kabur:



Kedua:





Ketiga:





Keempat:





Dalam gambar terlihat bahwa perubahan perbatasan untuk gambar yang jelas (ke-3 dan ke-4) lebih kuat daripada untuk gambar yang kabur (ke-1 dan ke-2).

Setelah perhitungan, kami mendapatkan koefisien:

[5.92918651681958,
2.672756123184502,
10.695051017699232,
11.901115749698139]

Koefisien mengkonfirmasi kesimpulan: semakin besar koefisien, semakin tajam foto.
Selain itu, gambar kedua kurang jelas dari yang pertama, yang tercermin dalam koefisien.

Fitur pendekatan


  • semakin tajam gambar, semakin kuat perubahan perbatasan, yang berarti semakin tinggi parameter;
  • untuk kebutuhan yang berbeda, kejelasan yang berbeda diperlukan. Oleh karena itu, Anda perlu menentukan batas-batas kejelasan Anda sendiri: di suatu tempat, koefisien foto yang cukup jelas akan di atas 7, di suatu tempat hanya di atas 10;
  • Koefisiennya tergantung pada kecerahan foto. Batas foto gelap akan berubah lebih lemah, yang berarti bahwa koefisiennya akan lebih kecil. Ternyata batas kejelasan harus ditentukan dengan mempertimbangkan pencahayaan, yaitu untuk foto standar;

Algoritme yang berfungsi dapat ditemukan di akun github saya .

All Articles