จะทำให้ภาพของฉลากบนขวดอาหารแบนได้อย่างไร?


40

ฉันต้องการถ่ายภาพฉลากบนขวดอาหารและสามารถเปลี่ยนมันได้ดังนั้นฉลากจะแบนโดยด้านขวาและซ้ายจะถูกปรับขนาดให้อยู่กับศูนย์กลางของภาพ

โดยหลักการแล้วฉันต้องการใช้ความแตกต่างระหว่างฉลากและพื้นหลังเพื่อค้นหาขอบและใช้การแก้ไข มิฉะนั้นฉันสามารถขอให้ผู้ใช้ระบุมุมและด้านข้างของภาพได้


ฉันกำลังมองหาเทคนิคทั่วไปและอัลกอริทึมเพื่อถ่ายภาพที่เอียงเป็นทรงกลม (รูปทรงกระบอกในกรณีของฉัน) และสามารถทำให้ภาพเรียบ ขณะนี้ภาพของฉลากที่ล้อมรอบขวดหรือขวดจะมีคุณสมบัติและข้อความที่หดตัวเมื่อลดขนาดไปทางซ้ายหรือขวาของภาพ เส้นที่แสดงถึงขอบของฉลากจะขนานกันตรงกลางของภาพและจะเอียงไปทางกันทางด้านขวาและด้านซ้ายสุดของฉลาก

หลังจากจัดการกับภาพแล้วฉันอยากจะเหลือสี่เหลี่ยมที่เกือบสมบูรณ์แบบซึ่งข้อความและฟีเจอร์ต่าง ๆ มีขนาดเท่ากันราวกับว่าฉันถ่ายภาพฉลากเมื่อไม่ได้อยู่ในขวดหรือขวด

นอกจากนี้ฉันต้องการถ้าเทคนิคสามารถตรวจจับขอบของฉลากโดยอัตโนมัติเพื่อใช้การแก้ไขที่เหมาะสม ไม่เช่นนั้นฉันจะต้องขอให้ผู้ใช้ระบุขอบเขตของป้ายกำกับ

ฉันได้ Googled แล้วและพบบทความเช่นนี้: เอกสารโค้งงอเรียบแต่ฉันกำลังมองหาบางสิ่งที่เรียบง่ายกว่าเล็กน้อย


Nikie มีสิ่งที่ดูเหมือนจะเป็นทางออกที่ครอบคลุมทั้งหมด แม้ว่าจะง่ายกว่ามาก แต่ถ้าคุณรู้ว่ากล้องอยู่ใน "สี่เหลี่ยม" กับโถเสมอโดยไม่มีพื้นหลังที่สับสน จากนั้นคุณจะพบขอบของขวดและใช้การแปลงตรีโกณมิติแบบง่าย (arcsine?) โดยไม่ต้องเล่นซอเพิ่มเติม เมื่อภาพแบนคุณสามารถแยกฉลากได้เอง
Daniel R Hicks

@ Daniel นั่นคือสิ่งที่ฉันได้ที่นี่ ในทางที่ดีจะคำนึงถึงการฉายภาพที่ไม่ขนานกันอย่างสมบูรณ์แบบ แต่ฉันก็ไม่ได้ทำ
Szabolcs

งานดีมาก แต่รหัสแสดงข้อผิดพลาดในระบบของฉัน ฉันใช้ matlab 2017a เข้ากันได้กับมัน ขอบคุณ
Satish Kumar

คำตอบ:


60

คำถามที่คล้ายกันถูกถามเกี่ยวกับ Mathematica.Stackexchange คำตอบของฉันมีการพัฒนาและได้ค่อนข้างนานในที่สุดดังนั้นฉันจะสรุปขั้นตอนวิธีที่นี่

บทคัดย่อ

แนวคิดพื้นฐานคือ:

  1. ค้นหาฉลาก
  2. ค้นหาชายแดนของฉลาก
  3. ค้นหาการแมปที่แมปพิกัดรูปภาพกับพิกัดรูปทรงกระบอกเพื่อที่จะแมปพิกเซลตามขอบด้านบนของป้ายกำกับไปที่ ([อะไร] / 0), พิกเซลตามขอบด้านขวาของ (1 / [อะไรก็ได้)) และอื่น ๆ
  4. แปลงภาพโดยใช้การทำแผนที่นี้

อัลกอริทึมใช้งานได้กับภาพที่:

  1. ฉลากสว่างกว่าพื้นหลัง (จำเป็นสำหรับการตรวจจับฉลาก)
  2. ป้ายชื่อเป็นรูปสี่เหลี่ยมผืนผ้า (ใช้เพื่อวัดคุณภาพของการแมป)
  3. jar เป็นแนวตั้ง (เกือบ) (ใช้เพื่อให้ฟังก์ชันการทำแผนที่ง่ายขึ้น)
  4. jar เป็นรูปทรงกระบอก (ใช้เพื่อให้ฟังก์ชันการทำแผนที่ง่าย ๆ )

อย่างไรก็ตามอัลกอริทึมเป็นแบบแยกส่วน อย่างน้อยในหลักการคุณสามารถเขียนการตรวจจับฉลากของคุณเองที่ไม่ต้องการพื้นหลังสีเข้มหรือคุณสามารถเขียนฟังก์ชั่นการวัดคุณภาพของคุณเองที่สามารถรับมือกับฉลากรูปไข่หรือแปดเหลี่ยม

ผล

ภาพเหล่านี้ได้รับการประมวลผลโดยอัตโนมัติอย่างสมบูรณ์เช่นอัลกอริธึมนำภาพต้นฉบับมาใช้งานได้สองสามวินาทีจากนั้นจะแสดงการแมป (ซ้าย) และภาพที่ไม่บิดเบี้ยว (ขวา):

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

ภาพถัดไปได้รับการประมวลผลด้วยอัลกอริทึมรุ่นที่แก้ไขแล้วคือผู้ใช้เลือกเส้นขอบซ้ายและขวาของ jar (ไม่ใช่ฉลาก) เนื่องจากความโค้งของฉลากไม่สามารถประเมินได้จากภาพในช็อตหน้า (เช่น อัลกอริทึมอัตโนมัติจะคืนค่ารูปภาพที่บิดเบี้ยวเล็กน้อย):

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

การดำเนินงาน:

1. ค้นหาฉลาก

ฉลากสว่างด้านหน้าของพื้นหลังสีดำดังนั้นฉันสามารถค้นหาได้อย่างง่ายดายโดยใช้ binarization:

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

ภาพ binarized

ฉันเลือกส่วนประกอบที่เชื่อมต่อที่ใหญ่ที่สุดและสมมติว่าเป็นฉลาก:

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 ที่อิง 1 โดยที่ y = 1 อยู่ด้านบนของ รูปภาพ แต่ฟังก์ชั่นการประมวลผลภาพทั้งหมดคาดหวังว่าพิกัดซึ่งเป็น 0-x {y, 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การโทรคือความพยายามในการป้องกันหมายเลขที่ซับซ้อนระหว่างการปรับให้เหมาะสม มีการแลกเปลี่ยนที่นี่: ในมือข้างหนึ่งฟังก์ชั่นควรจะใกล้เคียงกับการทำแผนที่ทรงกระบอกที่แน่นอนที่สุดเท่าที่จะทำได้เพื่อให้การบิดเบือนที่ต่ำที่สุด ในทางตรงกันข้ามถ้ามันมีความซับซ้อนมันก็ยากที่จะหาค่าที่เหมาะสมที่สุดสำหรับองศาอิสระโดยอัตโนมัติ (สิ่งที่ดีเกี่ยวกับการประมวลผลภาพด้วย Mathematica คือคุณสามารถเล่นกับแบบจำลองทางคณิตศาสตร์เช่นนี้ได้อย่างง่ายดายแนะนำคำเพิ่มเติมสำหรับการบิดเบือนที่แตกต่างกันและใช้ฟังก์ชั่นการเพิ่มประสิทธิภาพเดียวกันเพื่อให้ได้ผลลัพธ์สุดท้ายฉันไม่เคยทำอะไรเลย เช่นนั้นโดยใช้ OpenCV หรือ Matlab แต่ฉันไม่เคยลองใช้กล่องเครื่องมือสัญลักษณ์สำหรับ 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) เป็นต้น .

ตอนนี้ฉันสามารถบอก Mathematica เพื่อหาค่าสัมประสิทธิ์ที่ลดฟังก์ชั่นข้อผิดพลาดนี้ให้น้อยที่สุด ฉันสามารถสร้าง "การศึกษาที่คาดเดา" เกี่ยวกับสัมประสิทธิ์บางอย่าง (เช่นรัศมีและกึ่งกลางขวดในภาพ) ฉันใช้สิ่งเหล่านี้เป็นจุดเริ่มต้นของการเพิ่มประสิทธิภาพ:

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ฟังก์ชันของ Mathematica :

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ฟังก์ชันMathematica เพื่อบิดเบือนภาพตามแผนที่นี้:

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

ที่ให้ผลลัพธ์ตามที่แสดงด้านบน

รุ่นที่ช่วยเหลือด้วยตนเอง

อัลกอริทึมด้านบนเป็นแบบอัตโนมัติ ไม่จำเป็นต้องทำการปรับเปลี่ยน มันทำงานได้ดีพอสมควรตราบใดที่ภาพถูกถ่ายจากด้านบนหรือด้านล่าง แต่ถ้าเป็นภาพด้านหน้ารัศมีของขวดจะไม่สามารถประมาณได้จากรูปร่างของฉลาก ในกรณีเหล่านี้ฉันได้รับผลลัพธ์ที่ดีกว่ามากถ้าฉันให้ผู้ใช้ป้อนขอบซ้าย / ขวาของ jar ด้วยตนเองและตั้งค่าองศาอิสระที่สอดคล้องกันในแผนที่อย่างชัดเจน

รหัสนี้ช่วยให้ผู้ใช้เลือกขอบซ้าย / ขวา:

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
ถอดแว่นกันแดด ... พระมารดาของพระเจ้า ...
Spacey

คุณมีการอ้างอิงถึงการทำแผนที่ทรงกระบอกหรือไม่? และสมการสำหรับการทำแผนที่ผกผัน? @ niki-estner
Ita
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.