Bagaimana cara mendeteksi bahwa dua gambar "sama" walaupun ada satu pemotongan / rasio yang sedikit berbeda?

11

Saya memiliki dua gambar berbeda:

dalam 100px dengan masukkan deskripsi gambar di siniatau 400pxmasukkan deskripsi gambar di sini

dan

dalam lebar 100px masukkan deskripsi gambar di siniatau 400pxmasukkan deskripsi gambar di sini

Seperti yang Anda lihat, keduanya jelas "sama" dari sudut pandang manusia. Sekarang saya ingin mendeteksi secara program bahwa keduanya sama. Saya telah menggunakan sihir gambar melalui permata ruby ​​yang disebut rmagickseperti:

img1 = Magick::Image.from_blob(File.read("image_1.jpeg")).first
img2 = Magick::Image.from_blob(File.read("image_2.jpeg")).first

if img1.difference(img2).first < 4000.0 # I have found this to be a good threshold, but does not work for cropped images
  puts "they are the same!!!"
end

Meskipun ini bekerja dengan baik untuk gambar yang memiliki rasio / pemangkasan yang sama, itu tidak ideal ketika mereka memiliki pemangkasan yang sedikit berbeda dan telah diubah ukurannya menjadi lebar yang sama.

Apakah ada cara untuk melakukannya untuk gambar dengan pemotongan yang berbeda? Saya tertarik pada solusi di mana saya bisa mengatakan sesuatu seperti: Satu gambar terkandung di dalam yang lain dan mencakup sekitar 90% dari itu.

PS. Saya bisa mendapatkan gambar dalam resolusi lebih tinggi jika itu membantu (mis. Ganda)

Niels Kristian
sumber
2
Tidak yakin tentang RMagick, tetapi comparealat baris perintah ImageMagick memiliki -subimage-searchsaklar.
Stefan
Itu menarik, bagaimana perintah seperti itu terlihat?
Niels Kristian
2
Tidak pernah menggunakannya sendiri, mungkin ini bisa membantu: stackoverflow.com/q/29062811/477037
Stefan
Terima kasih, itu info yang bagus. Saya tidak tahu bagaimana melakukan ini dari ruby ​​...
Niels Kristian
1
Apakah gambarnya berkualitas rendah? Jika tidak, silakan bagikan versi gambar yang lebih besar, dengan kualitas yang lebih baik.
MH304

Jawaban:

6

Anda mungkin ingin melihat pencocokan fitur. Idenya adalah menemukan fitur dalam dua gambar dan mencocokkannya. Metode ini biasanya digunakan untuk menemukan templat (misalnya logo) di gambar lain. Fitur, pada dasarnya, dapat digambarkan sebagai hal-hal yang menurut manusia menarik dalam sebuah gambar, seperti sudut atau ruang terbuka. Ada banyak jenis teknik deteksi fitur di luar sana namun rekomendasi saya adalah menggunakan skala-invariant feature transform (SIFT) sebagai algoritma pendeteksian fitur. SIFT tidak berbeda dengan terjemahan gambar, penskalaan, rotasi, sebagian tidak berubah perubahan iluminasi, dan kuat untuk distorsi geometrik lokal. Ini tampaknya sesuai dengan spesifikasi Anda di mana gambar dapat memiliki rasio yang sedikit berbeda.

Dengan dua gambar yang Anda berikan, berikut ini upaya untuk mencocokkan fitur menggunakan pencocokan fitur FLANN . Untuk menentukan apakah kedua gambar itu sama, kita dapat mendasarkannya dari beberapa ambang yang telah ditentukan yang melacak jumlah kecocokan yang lulus uji rasio yang dijelaskan dalam Fitur Gambar Berbeda dari Keypoints Skala-Invarian oleh David G. Lowe . Penjelasan sederhana dari tes ini adalah bahwa tes rasio memeriksa apakah kecocokannya ambigu dan harus dihilangkan, Anda dapat memperlakukannya sebagai teknik penghilangan yang outlier. Kami dapat menghitung jumlah kecocokan yang lulus tes ini untuk menentukan apakah kedua gambar itu sama. Inilah hasil pencocokan fitur:

Matches: 42

Titik-titik mewakili semua kecocokan yang terdeteksi sementara garis hijau mewakili "kecocokan yang baik" yang lulus uji rasio. Jika Anda tidak menggunakan uji rasio maka semua poin akan ditarik. Dengan cara ini, Anda dapat menggunakan filter ini sebagai ambang untuk hanya menyimpan fitur yang paling cocok.


Saya menerapkannya dengan Python, saya tidak terlalu akrab dengan Rails. Semoga ini bisa membantu, semoga berhasil!

Kode

import numpy as np
import cv2

# Load images
image1 = cv2.imread('1.jpg', 0)
image2 = cv2.imread('2.jpg', 0)

# Create the sift object
sift = cv2.xfeatures2d.SIFT_create(700)

# Find keypoints and descriptors directly
kp1, des1 = sift.detectAndCompute(image2, None)
kp2, des2 = sift.detectAndCompute(image1, None)

# FLANN parameters
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)   # or pass empty dictionary
flann = cv2.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)

# Need to draw only good matches, so create a mask
matchesMask = [[0,0] for i in range(len(matches))]

count = 0
# Ratio test as per Lowe's paper (0.7)
# Modify to change threshold 
for i,(m,n) in enumerate(matches):
    if m.distance < 0.15*n.distance:
        count += 1
        matchesMask[i]=[1,0]

# Draw lines
draw_params = dict(matchColor = (0,255,0),
                   # singlePointColor = (255,0,0),
                   matchesMask = matchesMask,
                   flags = 0)

# Display the matches
result = cv2.drawMatchesKnn(image2,kp1,image1,kp2,matches,None,**draw_params)
print('Matches:', count)
cv2.imshow('result', result)
cv2.waitKey()
nathancy
sumber
2
Pendekatan super menarik, saya akan mencobanya dan kembali ...
Niels Kristian
PS. Saya memperbarui gambar dalam skala yang lebih besar
Niels Kristian
1
@nathancy Apakah itu sehingga pada contoh Anda, titik-titik hijau cocok, tetapi yang biru tidak? Sepertinya ada terlalu banyak titik yang tidak cocok?
Draco Ater
2
@DracoAter pertanyaan bagus, titik-titik biru mewakili semua kecocokan sementara kami hanya menggambar "kecocokan bagus" yang lulus uji rasio berwarna hijau. Jika Anda tidak menggunakan uji rasio maka semua poin akan ditarik tetapi kami memfilter menggunakan uji rasio untuk menggambar kecocokan "lebih baik". Dengan cara ini, OP dapat menggunakan tes ini sebagai ambang untuk menjaga fitur yang paling cocok. Jadi semua titik-titik biru adalah fitur yang SIFT ditemukan tetapi kita filter untuk menjaga orang-orang baik yang diambil dalam warna hijau
nathancy
Terima kasih. Persaingan sulit pada jawaban, banyak yang hebat :-)
Niels Kristian
4

Karena ImageMagick sangat tua, canggih, dan alat yang banyak fitur, akan sulit untuk membangun antarmuka yang mencakup sebagian besar fitur. Sebesar apa pun itu, rmagick tidak (dan tidak juga banyak upaya python telah diambil) mendekati menutup semua fitur.

Saya membayangkan untuk banyak kasus penggunaan, itu akan cukup aman dan jauh lebih mudah untuk hanya menjalankan metode baris perintah dan membacanya. Dalam ruby ​​itu akan terlihat seperti ini;

require 'open3'

def check_subimage(large, small)
    stdin, stdout, stderr, wait_thr = Open3.popen3("magick compare -subimage-search -metric RMSE #{large} #{small} temp.jpg")
    result = stderr.gets
    stderr.close
    stdout.close
    return result.split[1][1..-2].to_f < 0.2
end

if check_subimage('a.jpg', 'b.jpg')
    puts "b is a crop of a"
else
    puts "b is not a crop of a"
end

Saya akan membahas hal-hal penting dan kemudian berbicara tentang catatan tambahan.

Perintah ini menggunakan perbandingan magick untuk memeriksa apakah gambar kedua ( small) adalah subimage dari yang pertama ( large). Fungsi ini tidak memeriksa bahwa kecil benar-benar lebih kecil dari besar (tinggi dan lebar). Angka yang saya masukkan untuk kemiripan adalah 0,2 (20% kesalahan), dan nilai untuk gambar yang Anda berikan adalah sekitar 0,15. Anda mungkin ingin menyempurnakan ini! Saya menemukan bahwa gambar yang merupakan subset ketat mendapatkan kurang dari 0,01.

  • Jika Anda ingin lebih sedikit kesalahan (angka lebih kecil) pada kasus di mana Anda memiliki 90% tumpang tindih tetapi gambar kedua memiliki beberapa hal tambahan yang tidak pertama, Anda dapat menjalankannya sekali, lalu memotong gambar besar pertama ke tempat subimage berada. , lalu jalankan kembali dengan gambar yang dipangkas sebagai gambar "kecil" dan gambar "kecil" asli sebagai gambar besar.
  • Jika Anda benar-benar menginginkan antarmuka berorientasi objek yang bagus di Ruby, rmagick menggunakan MagicCore API. Perintah (tautan ke dokumen) ini mungkin adalah apa yang ingin Anda gunakan untuk mengimplementasikannya, dan Anda dapat membuka pr untuk rmagick atau mengemas paket sendiri.
  • Menggunakan open3 akan memulai utas ( lihat dokumen ). Menutup stderrdan stdouttidak "perlu" tetapi Anda seharusnya melakukannya.
  • Gambar "temp" yang merupakan argumen ketiga menentukan file untuk menampilkan analisis. Dengan tampilan cepat, saya tidak dapat menemukan cara untuk tidak memerlukannya, tetapi itu hanya menimpa secara otomatis dan bisa menjadi baik untuk disimpan untuk debugging. Sebagai contoh Anda, akan terlihat seperti ini;

masukkan deskripsi gambar di sini

  • Output penuh dalam format 10092,6 (0,154003) @ 0,31. Angka pertama adalah nilai rmse dari 655535, yang kedua (yang saya gunakan) adalah persentase dinormalisasi. Dua angka terakhir mewakili lokasi gambar asli dari mana gambar kecil dimulai.
  • Karena tidak ada sumber obyektif tentang kebenaran gambar yang "mirip", saya memilih RMSE (lihat lebih banyak opsi metrik di sini ). Ini adalah ukuran perbedaan antar nilai yang cukup umum. Hitungan Kesalahan Absolut (AE) mungkin tampak seperti ide yang bagus, namun tampaknya beberapa perangkat lunak pemangkasan tidak mempertahankan piksel dengan sempurna sehingga Anda mungkin harus menyesuaikan fuzz dan itu bukan nilai yang dinormalkan, jadi Anda harus membandingkan jumlah kesalahan dengan ukuran gambar dan yang lainnya.
Carol Chen
sumber
1
Itulah beberapa informasi hebat di sana, Carol. Terima kasih
Niels Kristian
Penasaran ingin tahu cara kerjanya untuk kasus Anda yang lain!
Carol Chen
1
Terima kasih atas jawaban yang sangat bagus. Jika saya bisa, saya telah memberi Anda hadiah 100p untuk yang ini juga :-)
Niels Kristian
3

Dapatkan histogram dari kedua gambar dan bandingkan. Ini akan bekerja dengan sangat baik untuk memotong dan Zoom kecuali ada perubahan yang terlalu drastis karena ini.

Ini lebih baik daripada pendekatan saat ini di mana Anda langsung mengurangi gambar. Namun pendekatan ini masih memiliki sedikit.

Raviteja Narra
sumber
Terima kasih atas sarannya, saya akan melihatnya.
Niels Kristian
Ini bukan jawaban yang sangat berguna karena tidak menunjukkan cara mencapai tujuan. Ini sama dengan "Google istilah ini dan cari tahu sendiri."
anothermh
Histogram adalah salah satu hal pertama yang dipelajari orang dalam pemrosesan gambar. Jika ada yang harus google, maka saya sangat minta maaf.
Raviteja Narra
3

Biasanya pencocokan template memiliki hasil yang baik dalam situasi ini. Pencocokan template adalah teknik untuk menemukan area gambar yang cocok (mirip) dengan gambar template (gambar kedua). Algoritma ini memberikan skor untuk posisi macthed terbaik dalam gambar sumber (yang kedua).

Dalam OpenCV menggunakan metode TM_CCOEFF_NORMED , berikan skor antara 0 dan 1. Jika skornya 1, itu berarti gambar template persis bagian (Rect) dari gambar sumber, tetapi jika Anda memiliki sedikit perubahan dalam penerangan atau perspektif antara gambar dua, skor akan lebih rendah dari 1.

Sekarang Dengan mempertimbangkan ambang batas untuk skor kesamaan, Anda dapat mengetahui apakah mereka sama atau tidak. Ambang itu dapat diperoleh dengan beberapa percobaan dan kesalahan pada beberapa sampel gambar. Saya mencoba gambar Anda dan mendapat skor 0,823863 . Berikut adalah kode (opencv C ++) dan area umum antara dua gambar, diperoleh dengan pencocokan:

masukkan deskripsi gambar di sini

Mat im2 = imread("E:/1/1.jpg", 1);
//Mat im2;// = imread("E:/1/1.jpg", 1);
Mat im1 = imread("E:/1/2.jpg", 1);

//im1(Rect(0, 0, im1.cols - 5, im1.rows - 5)).copyTo(im2);

int result_cols = im1.cols - im2.cols + 1;
int result_rows = im1.rows - im2.rows + 1;

Mat result = Mat::zeros(result_rows, result_cols, CV_32FC1);

matchTemplate(im1, im2, result, TM_CCOEFF_NORMED);

double minVal; double maxVal;
Point minLoc; Point maxLoc;
Point matchLoc;

minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());

cout << minVal << " " << maxVal << " " << minLoc << " " << maxLoc << "\n";
matchLoc = maxLoc;

rectangle(im1, matchLoc, Point(matchLoc.x + im2.cols, matchLoc.y + im2.rows), Scalar::all(0), 2, 8, 0);
rectangle(result, matchLoc, Point(matchLoc.x + im2.cols, matchLoc.y + im2.rows), Scalar::all(0), 2, 8, 0);

imshow("1", im1);
imshow("2", result);
waitKey(0);
MH304
sumber
Terima kasih atas jawaban yang sangat bagus. Jika saya bisa, saya telah memberi Anda hadiah 100p untuk yang ini juga :-)
Niels Kristian
2

Pertimbangkan metode find_similar_region . Gunakan yang lebih kecil dari dua gambar sebagai gambar target. Coba berbagai nilai untuk atribut fuzz pada gambar dan gambar target.

Pengguna RMagick
sumber
Terima kasih, namun saya tidak bisa membuatnya bekerja - bisakah Anda?
Niels Kristian