Membuat gambar mosaik

Tentunya Anda telah berulang kali melihat gambar seperti itu di Internet:

gambar

Saya memutuskan untuk menulis naskah universal untuk membuat gambar seperti itu.

Bagian teoretis


Mari kita bicara sedikit tentang bagaimana kita akan melakukan semua ini. Misalkan ada beberapa set gambar yang dapat kita gunakan untuk membuka kanvas, serta satu gambar, yang harus disajikan dalam bentuk mosaik. Kemudian kita perlu membagi gambar yang perlu dikonversi menjadi area yang identik, yang masing-masing kemudian diganti dengan gambar dari dataset dengan gambar.

Ini menimbulkan pertanyaan tentang bagaimana memahami gambar mana dari dataset yang harus kita ganti area tertentu. Tentu saja, ubin ideal pada area tertentu adalah area yang sama. Setiap area berukuran dapat diatur angka (di sini, setiap piksel berhubungan dengan tiga angka - komponen R, G, dan B). Dengan kata lain, masing-masing daerah didefinisikan oleh tensor tiga dimensi. Sekarang menjadi jelas bahwa untuk menentukan kualitas ubin daerah dengan gambar, asalkan ukurannya bertepatan, kita perlu menghitung beberapa fungsi kerugian. Dalam masalah ini, kita dapat mempertimbangkan MSE dari dua tensor:


Di sini - jumlah tanda, dalam kasus kami .

Namun, rumus ini tidak berlaku untuk kasus nyata. Faktanya adalah bahwa ketika dataset cukup besar, dan area di mana gambar asli dibagi cukup kecil, Anda harus melakukan banyak tindakan yang tidak dapat diterima, yaitu, kompres setiap gambar dari dataset ke ukuran area dan pertimbangkan MSE dengan karakteristik. Lebih tepatnya, dalam rumus ini hal buruknya adalah kita dipaksa untuk mengkompres secara mutlak setiap gambar untuk perbandingan, lebih dari sekali, tetapi angka yang sama dengan jumlah area di mana gambar asli dibagi.

Saya mengusulkan solusi berikut untuk masalah ini: kami akan mengorbankan sedikit kualitas dan sekarang kami akan mencirikan setiap gambar dari dataset dengan hanya 3 angka: rata-rata RGB pada gambar. Tentu saja, beberapa masalah muncul dari ini: pertama, sekarang ubin ideal daerah itu tidak hanya itu sendiri, tetapi, misalnya, juga terbalik (jelas bahwa ubin ini lebih buruk daripada yang pertama), dan kedua, setelah menghitung warna rata-rata, kami kita bisa mendapatkan R, G, dan B sehingga gambar bahkan tidak akan memiliki piksel dengan komponen-komponen seperti itu (dengan kata lain, sulit untuk mengatakan bahwa mata kita memandang gambar sebagai campuran dari semua warna). Namun, saya tidak menemukan cara yang lebih baik.

Ternyata sekarang kita hanya dapat sekali menghitung RGB rata-rata untuk gambar dari dataset, dan kemudian menggunakan informasi yang diterima.

Meringkas di atas, kita menemukan bahwa kita sekarang perlu memilih wilayah yang paling dekat dengan itu dari piksel RGB dari set, dan kemudian ubin wilayah dengan gambar dari dataset yang dimiliki media menengah seperti RGB milik. Untuk membandingkan area dan piksel, kami akan melakukan hal yang sama: kami mengubah area menjadi tiga angka dan menemukan rata-rata RGB terdekat. Ternyata kita hanya bisa temukan di set tersebut bahwa jarak Euclidean antara dua titik ini dalam ruang tiga dimensi akan minimal:



Dataset preprosesing


Anda dapat membangun dataset gambar Anda sendiri. Saya menggunakan gabungan dataset dengan gambar kucing dan anjing .

Seperti yang saya tulis di atas, kita sekali dapat menghitung nilai RGB rata-rata untuk gambar dari dataset dan hanya menyimpannya. Apa yang kita lakukan:

import os import cv2 import numpy as np import pickle items = {} # cv2      BGR,   RGB,     for path in os.listdir('dogs_images_dataset'): #      ,    for file in os.listdir(os.path.join('dogs_images_dataset', path)): file1 = os.path.join('dogs_images_dataset', path + '/' + file) img = np.array(cv2.cvtColor(cv2.imread(file1), cv2.COLOR_BGR2RGB)) r = round(img[:, :, 0].mean()) g = round(img[:, :, 1].mean()) b = round(img[:, :, 2].mean()) items[file1] = (r, g, b,) for file in os.listdir('cats_images_dataset'): #      ,          file1 = os.path.join('cats_images_dataset', file) img = np.array(cv2.cvtColor(cv2.imread(file1), cv2.COLOR_BGR2RGB)) r = round(img[:, :, 0].mean()) g = round(img[:, :, 1].mean()) b = round(img[:, :, 2].mean()) items[file1] = (r, g, b,) with open('data.pickle', 'wb') as f: pickle.dump(items, f) 

Script ini akan dieksekusi untuk waktu yang relatif lama, setelah itu informasi yang kita butuhkan akan disimpan dalam file data.pickle.

Mosaik


Akhirnya, mari beralih ke membuat mosaik. Pertama, kita akan menulis impor yang diperlukan, dan juga mendeklarasikan beberapa konstanta:

 import os import cv2 import pickle import numpy as np from math import sqrt PATH_TO_PICTURE = '' #       PICTURE = 'picture.png' #     VERTICAL_SECTION_SIZE = 7 #       HORIZONTAL_SECTION_SIZE = 7 #       

Kami mendapatkan data yang disimpan dari file:

 with open('data.pickle', 'rb') as f: items = pickle.load(f) 

Kami menggambarkan fungsi kerugian:

 def lost_function(r_segm, g_segm, b_segm, arg): r, g, b = arg[1] return sqrt((r - r_segm) ** 2 + (g - g_segm) ** 2 + (b - b_segm) ** 2) 

Buka gambar asli:

 file = os.path.join(PATH_TO_PICTURE, PICTURE) img = np.array(cv2.cvtColor(cv2.imread(file), cv2.COLOR_BGR2RGB)) size = img.shape x, y = size[0], size[1] 

Sekarang perhatikan bahwa ubin dimungkinkan jika dan hanya jika dimana - ukuran gambar asli, dan - Dimensi area ubin. Tentu saja kondisi di atas tidak selalu memuaskan. Oleh karena itu, kami akan memotong gambar asli ke ukuran yang sesuai, mengurangi dari ukuran gambar residu dari membagi dengan ukuran wilayah:

 img = cv2.resize(img, (y - (y % VERTICAL_SECTION_SIZE), x - (x % HORIZONTAL_SECTION_SIZE))) size = img.shape x, y = size[0], size[1] 

Sekarang kita melanjutkan langsung ke ubin:

 for i in range(x // HORIZONT AL_SECTION_SIZE): for j in range(y // VERTICAL_SECTION_SIZE): sect = img[i * HORIZONTAL_SECTION_SIZE:(i + 1) * HORIZONTAL_SECTION_SIZE, j * VERTICAL_SECTION_SIZE:(j + 1) * VERTICAL_SECTION_SIZE] r_mean, g_mean, b_mean = sect[:, :, 0].mean(), sect[:, :, 1].mean(), sect[:, :, 2].mean() 

Di sini, di garis kedua dari belakang, area gambar yang diinginkan dipilih, dan di baris terakhir komponen RGB rata-rata dipertimbangkan.

Sekarang perhatikan salah satu kalimat paling penting:

 current = sorted(items.items(), key=lambda argument: lost_function(r_mean, g_mean, b_mean, argument))[0] 

Baris ini mengurutkan semua gambar dari dataset dalam urutan menaik dengan nilai dari fungsi kerugian untuk mereka dan mendapatkan argumen.

Sekarang kita hanya perlu memotong gambar dan mengganti area dengan itu:

 resized = cv2.resize(cv2.cvtColor(cv2.imread(current[0]), cv2.COLOR_BGR2RGB), (VERTICAL_SECTION_SIZE, HORIZONTAL_SECTION_SIZE,)) img[i * HORIZONTAL_SECTION_SIZE:(i + 1) * HORIZONTAL_SECTION_SIZE, j * VERTICAL_SECTION_SIZE:(j + 1) * VERTICAL_SECTION_SIZE] = resized 

Dan akhirnya, tampilkan gambar yang dihasilkan di layar:

 img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) cv2.imshow('ImageWindow', img) cv2.waitKey(0) 

Sedikit lagi tentang fungsi kerugian


Secara umum, ada beberapa opsi untuk fungsi kerugian, yang masing-masing secara teoritis berlaku untuk masalah ini. Kualitas mereka hanya dapat dievaluasi secara empiris, apa yang dapat Anda lakukan :)



Kesimpulan


Inilah beberapa hasil saya:

Buka
gambar
gambar

Aslinya
gambar
gambar

Kode sumber lengkap, file data.pickle yang sudah dihitung, serta arsip dengan set data yang saya kumpulkan, dapat Anda lihat di repositori .

Source: https://habr.com/ru/post/id454828/


All Articles