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:
- Etiketi bulun.
- Etiketin sınırlarını bulun
- 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.
- Bu eşlemeyi kullanarak resmi dönüştür
Algoritma yalnızca aşağıdaki durumlarda görüntüler için çalışır:
- etiket arka plandan daha parlaktır (etiket tespiti için bu gereklidir)
- etiket dikdörtgendir (bu, bir eşlemenin kalitesini ölçmek için kullanılır)
- kavanoz (neredeyse) dikeydir (haritalama fonksiyonunu basit tutmak için kullanılır)
- 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:
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ü):
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]]]
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]]]
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}}]];
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 ( Position
dizinleri 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]]
FindMinimum
Hata 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 ContourPlot
fonksiyonunu 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]]]
4. Resmi dönüştürün
Son olarak, ImageForwardTransform
gö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}}]}]]]]
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]