Bir gıda kavanozundaki bir etiketin görüntüsünü nasıl düzleştirebilirim?


40

Bir kavanoz yiyecek üzerine etiketlerin fotoğraflarını çekmek ve bunları etiketin düz olması için sağa ve sola yeniden görüntünün merkezinde olacak şekilde yeniden boyutlandırmak için dönüştürmek istiyorum.

İdeal olarak, kenarları bulmak ve düzeltmeyi uygulamak için etiket ile arka plan arasındaki kontrastı kullanmak isterim. Aksi takdirde, kullanıcıdan görüntünün köşelerini ve kenarlarını bir şekilde tanımlamasını isteyebilirim.


Küresel olarak çarpık (benim durumumda silindirik) bir görüntü çekmek ve görüntüyü düzleştirmek için genel teknikler ve algoritmalar arıyorum. Şu anda bir kavanoz veya şişenin etrafına sarılmış bir etiketin görüntüsü, görüntünün sağında veya solunda olduğu gibi küçülen özelliklere ve metne sahip olacaktır. Ayrıca, etiketin kenarını gösteren çizgiler, yalnızca görüntünün merkezinde paralel olacak ve etiketin sağ ve sol ucunda birbirine doğru bükülecektir.

Görüntüyü değiştirdikten sonra, kavanozun veya şişenin üzerinde değilken etiketin bir resmini çektiğim gibi, metnin ve özelliklerin eşit büyüklükte olduğu neredeyse mükemmel bir dikdörtgenle bırakılmak istiyorum.

Ayrıca, tekniğin uygun düzeltmeyi uygulamak için etiketin kenarlarını otomatik olarak tespit edebilmesini isterim. Aksi takdirde, kullanıcıdan sınır etiketlerini belirtmesini istemem gerekir.

Ben zaten Googled ve bunun gibi makaleler buldum: kavisli belgeleri düzleştirmek , ancak ihtiyaçlarım basit eğri içeren etiketler için biraz daha basit bir şey arıyorum.


Nikie, her şeyi kapsayan bir çözüm gibi görünen bir şeye sahiptir. Ancak, kameranın kavanozun her zaman "kare" olduğunu biliyorsanız, kafa karıştırıcı bir arka plan olmadan, çok daha basitleşir. Sonra kavanozun kenarlarını bulup, basit bir trigonometrik (ark)? Görüntü düzleştiğinde, etiketi kendinden izole edebilirsiniz.
Daniel R Hicks

@Daniel Burada ne yaptım . İdeal olarak, paralel olmayan projeksiyonu da hesaba katardım, ama yapmadım.
Szabolcs

iş çok iyi. ama kod sistemimde hata gösteriyor. matlab 2017a kullanıyorum bununla uyumlu. teşekkür ederim
Satish Kumar

Yanıtlar:


60

Bir benzer soru Mathematica.Stackexchange üzerinde istendi . Oradaki cevabım gelişti ve sonunda oldukça uzadı, bu yüzden burada algoritmayı özetleyeceğim.

Soyut, Özet

Temel fikir şudur:

  1. Etiketi bulun.
  2. Etiketin sınırlarını bulun
  3. Görüntü koordinatlarını silindir koordinatlarına eşleyen bir eşleme bulun, böylece etiketin üst kenarındaki pikselleri ([herhangi bir şey / 0), sağ kenardaki pikselleri (1 / [herhangi bir şey]) ve benzeriyle eşler.
  4. Bu eşlemeyi kullanarak resmi dönüştür

Algoritma yalnızca aşağıdaki durumlarda görüntüler için çalışır:

  1. etiket arka plandan daha parlaktır (etiket tespiti için bu gereklidir)
  2. etiket dikdörtgendir (bu, bir eşlemenin kalitesini ölçmek için kullanılır)
  3. kavanoz (neredeyse) dikeydir (haritalama fonksiyonunu basit tutmak için kullanılır)
  4. kavanoz silindiriktir (bu haritalama fonksiyonunu basit tutmak için kullanılır)

Ancak, algoritma modülerdir. En azından ilke olarak, koyu bir arka plan gerektirmeyen kendi etiket algılamanızı yazabilir ya da eliptik veya sekizgen etiketlerle başa çıkabilen kendi kalite ölçüm fonksiyonunuzu yazabilirsiniz.

Sonuçlar

Bu görüntüler tamamen otomatik olarak işlendi, yani algoritma kaynak görüntüyü alır, birkaç saniye çalışır, sonra eşlemeyi (solda) ve bozulmamış görüntüyü (sağda) gösterir:

görüntü tanımını buraya girin

görüntü tanımını buraya girin

görüntü tanımını buraya girin

görüntü tanımını buraya girin

görüntü tanımını buraya girin

görüntü tanımını buraya girin

görüntü tanımını buraya girin

Sonraki görüntüler algoritmanın değiştirilmiş bir versiyonuyla işlendi, kullanıcı kavanozun sol ve sağ kenarlıklarını seçti (etiketin değil), çünkü etiketin eğriliği bir ön çekimdeki görüntüden tahmin edilemez (yani; tam otomatik algoritma biraz bozuk görüntüyü döndürürdü):

görüntü tanımını buraya girin

görüntü tanımını buraya girin

Uygulama:

1. Etiketi bulun

Etiket koyu arka planın önünde parlaktır, bu yüzden binarizasyonu kullanarak kolayca bulabilirim:

src = Import["http://i.stack.imgur.com/rfNu7.png"];
binary = FillingTransform[DeleteBorderComponents[Binarize[src]]]

ikili görüntü

En büyük bağlı bileşeni seçerim ve etiketin o olduğunu varsayarım:

labelMask = Image[SortBy[ComponentMeasurements[binary, {"Area", "Mask"}][[All, 2]], First][[-1, 2]]]

en büyük bileşen

2. Etiketin sınırlarını bulun

Sonraki adım: basit türev evrişim maskeleri kullanarak üst / alt / sol / sağ sınırları bulun:

topBorder = DeleteSmallComponents[ImageConvolve[labelMask, {{1}, {-1}}]];
bottomBorder = DeleteSmallComponents[ImageConvolve[labelMask, {{-1}, {1}}]];
leftBorder = DeleteSmallComponents[ImageConvolve[labelMask, {{1, -1}}]];
rightBorder = DeleteSmallComponents[ImageConvolve[labelMask, {{-1, 1}}]];

görüntü tanımını buraya girin

Bu, bu dört görüntüden birinde tüm beyaz pikselleri bulan ve dizinleri koordinatlara dönüştüren küçük bir yardımcı işlevdir ( Positiondizinleri döndürür ve dizinler 1 tabanlı {y, x} -tuples'tir, burada y = 1; Ancak, tüm görüntü işleme işlevleri, 0 tabanlı {x, y} -tuples olan koordinatları bekler; burada y = 0, görüntünün en altındadır):

{w, h} = ImageDimensions[topBorder];
maskToPoints = Function[mask, {#[[2]]-1, h - #[[1]]+1} & /@ Position[ImageData[mask], 1.]];

3. Görüntüden silindir koordinatlarına bir eşleme bulun

Şimdi etiketin üst, alt, sol ve sağ kenarlıklarından oluşan dört ayrı koordinat listesine sahibim. Görüntü koordinatlarından silindir koordinatlarına bir eşleme tanımlarım:

arcSinSeries = Normal[Series[ArcSin[\[Alpha]], {\[Alpha], 0, 10}]]
Clear[mapping];
mapping[{x_, y_}] := 
   {
    c1 + c2*(arcSinSeries /. \[Alpha] -> (x - cx)/r) + c3*y + c4*x*y, 
    top + y*height + tilt1*Sqrt[Clip[r^2 - (x - cx)^2, {0.01, \[Infinity]}]] + tilt2*y*Sqrt[Clip[r^2 - (x - cx)^2, {0.01, \[Infinity]}]]
   }

Bu, kaynak görüntüdeki X / Y koordinatlarını silindirik koordinatlara eşleyen bir silindirik haritalamadır. Haritalama, yükseklik / yarıçap / merkez / perspektif / eğim için 10 derece serbestliğe sahiptir. Ark serisine yaklaşmak için Taylor serisini kullandım, çünkü ArcSin ile çalışan optimizasyonu doğrudan elde edemedim. Clipçağrılar, optimizasyon sırasında karmaşık numaraları önlemeye yönelik benim geçici girişimim. Burada bir takas var: Bir yandan, fonksiyon mümkün olan en düşük bozulmayı sağlamak için mümkün olduğu kadar silindirik bir haritalamaya yakın olmalıdır. Öte yandan, eğer karmaşıksa, serbestlik dereceleri için otomatik olarak en uygun değerleri bulmak çok zorlaşır. (Mathematica ile görüntü işleme yapmanın güzel tarafı, bunun gibi matematiksel modellerle kolayca oynayabilir, farklı çarpıtmalar için ek terimler ekleyebilir ve son sonuçları elde etmek için aynı optimizasyon işlevlerini kullanabilirsiniz. OpenCV veya Matlab kullanarak olduğu gibi.Ama Matlab için sembolik araç kutusunu hiç denemedim, belki de daha kullanışlı hale getirir.)

Sonra bir görüntünün kalitesini ölçen bir "hata fonksiyonu" tanımladım -> silindir koordinat haritalaması. Sınır pikselleri için sadece kare hataların toplamı:

errorFunction =
  Flatten[{
    (mapping[#][[1]])^2 & /@ maskToPoints[leftBorder],
    (mapping[#][[1]] - 1)^2 & /@ maskToPoints[rightBorder],
    (mapping[#][[2]] - 1)^2 & /@ maskToPoints[topBorder],
    (mapping[#][[2]])^2 & /@ maskToPoints[bottomBorder]
    }];

Bu hata işlevi bir eşlemenin "kalitesini" ölçer: Sol kenardaki noktalar (0 / [herhangi bir şey]) ile eşlenirse, üst kenardaki pikseller ([herhangi bir şey / 0) ile eşlenirse vb. .

Şimdi, Mathematica'ya bu hata fonksiyonunu en aza indiren katsayıları bulmasını söyleyebilirim. Bazı katsayılar hakkında "eğitimli tahminler" yapabilirim (örn. Görüntüdeki kavanozun yarıçapı ve merkezi). Bunları optimizasyonun başlangıç ​​noktaları olarak kullanıyorum:

leftMean = Mean[maskToPoints[leftBorder]][[1]];
rightMean = Mean[maskToPoints[rightBorder]][[1]];
topMean = Mean[maskToPoints[topBorder]][[2]];
bottomMean = Mean[maskToPoints[bottomBorder]][[2]];
solution = 
 FindMinimum[
   Total[errorFunction], 
    {{c1, 0}, {c2, rightMean - leftMean}, {c3, 0}, {c4, 0}, 
     {cx, (leftMean + rightMean)/2}, 
     {top, topMean}, 
     {r, rightMean - leftMean}, 
     {height, bottomMean - topMean}, 
     {tilt1, 0}, {tilt2, 0}}][[2]]

FindMinimumHata işlevini en aza indiren haritalama işlevimin 10 serbestlik derecesi için değerler bulur. Genel haritalamayı ve bu çözümü birleştirin ve X / Y görüntü koordinatlarından, etiket alanına uyan bir harita alıyorum. Bu haritayı Mathematica'nın ContourPlotfonksiyonunu kullanarak görselleştirebilirim :

Show[src,
 ContourPlot[mapping[{x, y}][[1]] /. solution, {x, 0, w}, {y, 0, h}, 
  ContourShading -> None, ContourStyle -> Red, 
  Contours -> Range[0, 1, 0.1], 
  RegionFunction -> Function[{x, y}, 0 <= (mapping[{x, y}][[2]] /. solution) <= 1]],
 ContourPlot[mapping[{x, y}][[2]] /. solution, {x, 0, w}, {y, 0, h}, 
  ContourShading -> None, ContourStyle -> Red, 
  Contours -> Range[0, 1, 0.2],
  RegionFunction -> Function[{x, y}, 0 <= (mapping[{x, y}][[1]] /. solution) <= 1]]]

görüntü tanımını buraya girin

4. Resmi dönüştürün

Son olarak, ImageForwardTransformgörüntüyü bu haritalamaya göre bozmak için Mathematica'nın işlevini kullanıyorum :

ImageForwardTransformation[src, mapping[#] /. solution &, {400, 300}, DataRange -> Full, PlotRange -> {{0, 1}, {0, 1}}]

Bu yukarıda gösterilen sonuçları verir.

Manuel destekli sürüm

Yukarıdaki algoritma tam otomatik. Ayar gerekmez. Resim yukarıdan veya aşağıdan çekildiği sürece oldukça iyi çalışır. Ancak önden bir atışsa, kavanozun yarıçapı etiketin şeklinden tahmin edilemez. Bu durumlarda, kullanıcının kavanozun sol / sağ kenarlıklarını manuel olarak girmesine izin verir ve eşleştirmede karşılık gelen serbestlik derecelerini açıkça ayarlarsam çok daha iyi sonuçlar alırım.

Bu kod kullanıcının sol / sağ sınırlarını seçmesine izin verir:

LocatorPane[Dynamic[{{xLeft, y1}, {xRight, y2}}], 
 Dynamic[Show[src, 
   Graphics[{Red, Line[{{xLeft, 0}, {xLeft, h}}], 
     Line[{{xRight, 0}, {xRight, h}}]}]]]]

LocatorPane

Bu, merkez ve yarıçapa açıkça verilen alternatif optimizasyon kodudur.

manualAdjustments = {cx -> (xLeft + xRight)/2, r -> (xRight - xLeft)/2};
solution = 
  FindMinimum[
   Total[minimize /. manualAdjustments], 
    {{c1, 0}, {c2, rightMean - leftMean}, {c3, 0}, {c4, 0}, 
     {top, topMean}, 
     {height, bottomMean - topMean}, 
     {tilt1, 0}, {tilt2, 0}}][[2]]
solution = Join[solution, manualAdjustments]

11
Güneş gözlüklerini çıkarır ... tanrının annesi ...
Spacey

Silindirik haritalamaya bir referansınız var mı? Ve belki de ters haritalama için denklemler? @ niki-estner
Ita,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.