फूड जार पर एक लेबल की छवि को कैसे सपाट करें?


40

मैं खाने के जार पर लेबल की तस्वीरें लेना चाहता हूं, और उन्हें बदलने में सक्षम हूं ताकि लेबल सपाट हो, दाईं और बाईं ओर छवि के केंद्र के साथ भी आकार दिया जा सके।

आदर्श रूप से, मैं किनारों को खोजने और सुधार को लागू करने के लिए लेबल और पृष्ठभूमि के बीच के विपरीत का उपयोग करना चाहूंगा। अन्यथा, मैं उपयोगकर्ता को किसी भी तरह से कोने और छवि के किनारों की पहचान करने के लिए कह सकता हूं।


मैं एक ऐसी छवि लेने के लिए सामान्य तकनीकों और एल्गोरिदम की तलाश कर रहा हूं जो गोलाकार रूप से तिरछी हो (मेरे मामले में बेलनाकार) और छवि को समतल कर सकती है। वर्तमान में एक लेबल की छवि जो एक जार या बोतल के चारों ओर लपेटी जाती है, उसमें विशेषताएं और पाठ होंगे जो छवि के दाईं ओर या बाईं ओर पीछे हटते हैं। इसके अलावा जो लाइनें लेबल के किनारे को दर्शाती हैं, वे केवल छवि के केंद्र में समानांतर होंगी, और लेबल के दाईं और बाईं ओर प्रत्येक-दूसरे की ओर तिरछी होंगी।

छवि में हेरफेर करने के बाद, मैं लगभग पूर्ण आयत के साथ छोड़ना चाहूंगा जहां पाठ और विशेषताएं समान रूप से आकार की हैं, जैसे कि जब मैंने जार या बोतल पर नहीं था तो लेबल की एक तस्वीर ली थी।

इसके अलावा, मुझे यह पसंद आएगा कि उपयुक्त सुधार को लागू करने के लिए तकनीक स्वचालित रूप से लेबल के किनारों का पता लगा सके। अन्यथा मुझे अपने उपयोगकर्ता से लेबल सीमाओं को इंगित करने के लिए कहना होगा।

मैंने पहले से ही Googled और इस तरह के लेख पाए हैं: चपटा घुमावदार दस्तावेज़ , लेकिन मैं कुछ सरल की तलाश कर रहा हूं, क्योंकि मेरी ज़रूरत एक साधारण वक्र के साथ लेबल के लिए है।


निकी के पास एक सर्वव्यापी समाधान प्रतीत होता है। यह बहुत सरल हो जाता है, हालांकि, अगर आप जानते हैं कि कैमरा हमेशा जार को "स्क्वायर" होता है, जिसमें कोई भ्रमित पृष्ठभूमि नहीं होती है। फिर आप जार के किनारों को ढूंढते हैं और सरल त्रिकोणमितीय (आर्सेन?) परिवर्तन को लागू करते हैं, बिना किसी अतिरिक्त फिसलन के। एक बार छवि के समतल हो जाने के बाद आप लेबल को अलग कर सकते हैं।
डैनियल आर हिक्स

@ डैनियल वही है जो मैंने यहां किया था । आदर्श रूप से एक के रूप में अच्छी तरह से नहीं पूरी तरह से समानांतर प्रक्षेपण को ध्यान में रखना होगा, लेकिन मैं नहीं था।
शेजाबल्क्स

काम बहुत अच्छा है। लेकिन कोड मेरे सिस्टम में त्रुटि दिखा रहा है। मैं matlab 2017a का उपयोग कर रहा हूं क्या यह इसके अनुरूप है।
सतीश कुमार

जवाबों:


60

Mathematica.Stackexchange पर एक समान प्रश्न पूछा गया था । वहाँ पर मेरा जवाब विकसित हुआ और अंत में काफी लंबा रहा, इसलिए मैं यहाँ एल्गोरिथम को संक्षेप में प्रस्तुत करूँगा।

सार

मूल विचार है:

  1. लेबल का पता लगाएं।
  2. लेबल की सीमाओं का पता लगाएं
  3. एक मानचित्रण खोजें जो मानचित्र छवि सिलेंडर के निर्देशांक से समन्वयित करता है ताकि यह पिक्सेल को लेबल की शीर्ष सीमा ([कुछ भी] / 0), पिक्सेल को सही सीमा के साथ (1 / [कुछ भी)) और इतने पर मैप कर सके।
  4. इस मैपिंग का उपयोग करके छवि को परिवर्तित करें

एल्गोरिथ्म केवल उन चित्रों के लिए काम करता है जहां:

  1. लेबल पृष्ठभूमि से उज्जवल है (यह लेबल पहचान के लिए आवश्यक है)
  2. लेबल आयताकार है (इसका उपयोग मैपिंग की गुणवत्ता को मापने के लिए किया जाता है)
  3. जार है (लगभग) वर्टिकल (इसका उपयोग मैपिंग फंक्शन को सरल रखने के लिए किया जाता है)
  4. जार बेलनाकार है (इसका उपयोग मैपिंग फ़ंक्शन को सरल रखने के लिए किया जाता है)

हालाँकि, एल्गोरिथ्म मॉड्यूलर है। कम से कम सिद्धांत रूप में, आप अपने स्वयं के लेबल का पता लगा सकते हैं जिसमें एक अंधेरे पृष्ठभूमि की आवश्यकता नहीं होती है, या आप अपने स्वयं के गुणवत्ता माप फ़ंक्शन लिख सकते हैं जो अण्डाकार या अष्टकोणीय लेबल के साथ सामना कर सकते हैं।

परिणाम

इन छवियों को पूरी तरह से स्वचालित रूप से संसाधित किया गया था, अर्थात एल्गोरिथ्म स्रोत छवि लेता है, कुछ सेकंड के लिए काम करता है, फिर मैपिंग (बाएं) और संयुक्त राष्ट्र-विकृत छवि (दाएं) दिखाता है:

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

अगली छवियों को एल्गोरिथ्म के एक संशोधित संस्करण के साथ संसाधित किया गया था, उपयोगकर्ता जार (लेबल नहीं) की बाईं और दाईं सीमाओं का चयन करते थे, क्योंकि लेबल की वक्रता का अनुमान ललाट शॉट (यानी) में छवि से नहीं लगाया जा सकता है पूरी तरह से स्वचालित एल्गोरिथ्म उन छवियों को वापस करेगा जो थोड़ा विकृत हैं):

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

कार्यान्वयन:

1. लेबल का पता लगाएं

लेबल एक काले रंग की पृष्ठभूमि के सामने उज्ज्वल है, इसलिए मैं इसे आसानी से बिनाराइजेशन का उपयोग करके पा सकता हूं:

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

द्विपरीकृत छवि

मैं बस सबसे बड़ा जुड़ा घटक चुनता हूं और मान लेता हूं कि यह लेबल है:

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

सबसे बड़ा घटक

2. लेबल की सीमाओं का पता लगाएं

अगला चरण: सरल व्युत्पन्न सजा मास्क का उपयोग करके ऊपर / नीचे / बाएँ / दाएँ सीमाएँ खोजें:

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

यहाँ छवि विवरण दर्ज करें

यह एक छोटा सहायक कार्य है जो इन चार छवियों में से एक में सभी सफेद पिक्सेल पाता है और सूचकांकों को निर्देशांक में परिवर्तित करता है (सूचकांकों को Positionइंगित करता है, और सूचकांक 1-आधारित {y, x} -tuples, जहां y = 1 शीर्ष पर है) छवि। लेकिन सभी इमेज प्रोसेसिंग फ़ंक्शंस समन्वय की उम्मीद करते हैं, जो 0-आधारित {x, y} -tuples, जहां y = 0 छवि के नीचे है):

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

3. छवि से सिलेंडर निर्देशांक के लिए एक मानचित्रण का पता लगाएं

अब मेरे पास लेबल के ऊपर, नीचे, बाएँ, दाएँ सीमाओं के निर्देशांक की चार अलग-अलग सूचियाँ हैं। मैं एक छवि मानचित्रण से निर्देशांक सिलेंडर निर्देशांक को परिभाषित करता हूं:

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]}]]
   }

यह एक बेलनाकार मानचित्रण है, जो स्रोत छवि में X / Y-निर्देशांक को बेलनाकार निर्देशांक में मैप करता है। मैपिंग में ऊंचाई / त्रिज्या / केंद्र / परिप्रेक्ष्य / झुकाव के लिए 10 डिग्री की स्वतंत्रता है। मैंने आर्क सीन्स को अनुमानित करने के लिए टेलर श्रृंखला का उपयोग किया, क्योंकि मुझे सीधे आर्किन के साथ काम करने का अनुकूलन नहीं मिला। Clipकॉल अनुकूलन के दौरान जटिल संख्या को रोकने के लिए मेरा तदर्थ प्रयास है। यहां एक व्यापार बंद है: एक तरफ, फ़ंक्शन संभव के रूप में एक सटीक बेलनाकार मानचित्रण के करीब होना चाहिए, ताकि सबसे कम संभव विरूपण दिया जा सके। दूसरी ओर, यदि यह जटिल है, तो स्वत: स्वतंत्रता की डिग्री के लिए इष्टतम मान प्राप्त करना बहुत कठिन हो जाता है। (मैथेमेटिका के साथ इमेज प्रोसेसिंग करने के बारे में अच्छी बात यह है कि आप गणितीय मॉडलों के साथ इस तरह से बहुत आसानी से खेल सकते हैं, विभिन्न विकृतियों के लिए अतिरिक्त शर्तें पेश कर सकते हैं और अंतिम परिणामों को प्राप्त करने के लिए समान अनुकूलन कार्यों का उपयोग कर सकते हैं। मैं कभी भी कुछ नहीं कर पाया। OpenCV या Matlab का उपयोग करना पसंद है। लेकिन मैंने कभी मतलाब के लिए प्रतीकात्मक टूलबॉक्स की कोशिश नहीं की, हो सकता है कि यह इसे बहुत उपयोगी बनाता हो।)

अगला मैं एक "त्रुटि फ़ंक्शन" को परिभाषित करता हूं जो एक छवि की गुणवत्ता को मापता है -> सिलेंडर समन्वय मैपिंग। यह सीमा पिक्सल के लिए चुकता त्रुटियों का योग है:

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

यह त्रुटि फ़ंक्शन मैपिंग की "गुणवत्ता" को मापता है: यह सबसे कम है यदि बाईं सीमा पर स्थित बिंदुओं (0 / [कुछ भी]) पर मैप किया जाता है, शीर्ष सीमा पर पिक्सल को ([कुछ भी] / 0) और इतने पर मैप किया जाता है ।

अब मैं गणितज्ञों को गुणांक खोजने के लिए कह सकता हूं जो इस त्रुटि फ़ंक्शन को कम करते हैं। मैं कुछ गुणांक के बारे में "शिक्षित अनुमान" बना सकता हूं (जैसे कि छवि में त्रिज्या और जार का केंद्र)। मैं अनुकूलन के शुरुआती बिंदुओं के रूप में इनका उपयोग करता हूं:

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मेरी मैपिंग फ़ंक्शन की स्वतंत्रता की 10 डिग्री के लिए मान पाता है जो त्रुटि फ़ंक्शन को कम करता है। जेनेरिक मैपिंग और इस समाधान को मिलाएं और मुझे X / Y छवि निर्देशांक से एक मैपिंग मिलती है, जो लेबल क्षेत्र को फिट करती है। मैं गणितज्ञ के ContourPlotकार्य का उपयोग करके इस मानचित्रण की कल्पना कर सकता हूं :

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. छवि को बदलना

अंत में, मैं ImageForwardTransformइस मैपिंग के अनुसार छवि को विकृत करने के लिए मैथेमेटिका के फ़ंक्शन का उपयोग करता हूं :

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

जैसा कि ऊपर दिखाया गया है कि परिणाम देता है।

मैन्युअल रूप से सहायता प्राप्त संस्करण

ऊपर एल्गोरिथ्म पूर्ण-स्वचालित है। कोई समायोजन की आवश्यकता है। यह यथोचित रूप से तब तक काम करता है जब तक कि तस्वीर ऊपर या नीचे से ली गई हो। लेकिन अगर यह एक ललाट शॉट है, तो जार की त्रिज्या का अनुमान लेबल के आकार से नहीं लगाया जा सकता है। इन मामलों में, मुझे बहुत बेहतर परिणाम मिलते हैं यदि मैं उपयोगकर्ता को जार की बाईं / दाईं सीमाओं को मैन्युअल रूप से दर्ज करने देता हूं, और मैपिंग में स्वतंत्रता की संबंधित डिग्री को स्पष्ट रूप से सेट करता हूं।

यह कोड उपयोगकर्ता को बाईं / दाईं सीमाओं का चयन करने देता है:

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

LocatorPane

यह वैकल्पिक अनुकूलन कोड है, जहां केंद्र और त्रिज्या स्पष्ट रूप से दिए गए हैं।

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
धूप के चश्मे को हटाता है ... भगवान की माँ ...
स्पेसी

क्या आप बेलनाकार मानचित्रण का संदर्भ रखते हैं? और शायद उलटा मानचित्रण के लिए समीकरण? @ निकी-एस्टनर
इटा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.