식품 용기에 라벨 이미지를 평평하게하는 방법?


40

식품 한 병에 라벨 사진을 찍고 라벨이 평평하고 오른쪽과 왼쪽의 크기가 이미지의 중심과 동일하게 조정되도록 라벨을 변형 할 수 있기를 원합니다.

이상적으로는 가장자리를 찾아서 수정을 적용하기 위해 레이블과 배경 사이의 대비를 사용하고 싶습니다. 그렇지 않으면 사용자에게 이미지의 모서리와 측면을 식별하도록 요청할 수 있습니다.


구형으로 기울어 진 (내 경우에는 원통형으로) 이미지를 평평하게 할 수있는 일반적인 기술과 알고리즘을 찾고 있습니다. 현재 병이나 병에 싸인 레이블의 이미지에는 이미지의 오른쪽이나 왼쪽으로 물러나면서 축소되는 기능과 텍스트가 있습니다. 또한 레이블의 가장자리를 나타내는 선은 이미지 중앙에서만 평행을 이루며 레이블의 오른쪽과 왼쪽 끝에서 서로를 향해 기울어집니다.

이미지를 조작 한 후, 항아리 나 병에없는 레이블의 사진을 찍는 것처럼 텍스트와 기능의 크기가 균일 한 거의 완벽한 사각형으로 남겨두고 싶습니다.

또한 적절한 보정을 적용하기 위해 기술이 라벨의 가장자리를 자동으로 감지 할 수 있다면 좋겠습니다. 그렇지 않으면 사용자에게 레이블 경계를 표시하도록 요청해야합니다.

나는 이미 구글 검색하고 다음과 같은 기사를 찾았습니다 : 곡선 문서를 평평하게 하지만, 간단한 곡선이있는 레이블이 필요하기 때문에 조금 더 간단한 것을 찾고 있습니다.


Nikie는 포괄적 인 솔루션으로 보입니다. 그러나 카메라가 항상 혼란스러운 배경없이 항아리에 "사각형"인 것을 알고 있으면 훨씬 간단 해집니다. 그런 다음 항아리의 가장자리를 찾아 추가로 들리지 않고 간단한 삼각법 (arcsine?) 변환을 적용하십시오. 이미지가 평평 해지면 라벨 자체를 분리 할 수 ​​있습니다.
Daniel R은

@Daniel 그게 내가 여기서 한 일 이다. 완벽하게 평행하지 않은 투영도 고려하는 것이 이상적이지만, 그렇지 않았습니다.
Szabolcs가

작업은 매우 좋습니다. 하지만 내 시스템에서 오류를 표시하는 코드입니다. 나는 matlab 2017a를 사용하고 있습니다. , 감사합니다
사티쉬 쿠마르에게

답변:


60

비슷한 질문이 Mathematica.Stackexchange에 질문을 받았다 . 저의 대답은 진화했고 결국 꽤 길었습니다. 그래서 여기에 알고리즘을 요약하겠습니다.

요약

기본 아이디어는 다음과 같습니다.

  1. 라벨을 찾으십시오.
  2. 라벨의 테두리를 찾습니다
  3. 이미지 좌표를 원통 좌표에 매핑하여 레이블의 위쪽 테두리를 따라 픽셀을 ([anything] / 0)으로, 오른쪽 테두리를 따라 픽셀을 (1 / [anything])으로 매핑하는 매핑을 찾습니다.
  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}}]];

여기에 이미지 설명을 입력하십시오

이것은이 4 개의 이미지 중 하나에서 모든 흰색 픽셀을 찾고 인덱스를 좌표로 변환하는 작은 도우미 함수입니다 (지수를 Position반환하고 인덱스는 1 기반 {y, x} 튜플입니다. 여기서 y = 1은 맨 위에 있음) 그러나 모든 이미지 처리 함수는 0 기반 {x, y}-튜플 인 좌표를 필요로합니다. 여기서 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 자유도를 갖습니다. ArcSin으로 직접 작업하는 최적화를 얻을 수 없기 때문에 Taylor 시리즈를 사용하여 아크 사인을 근사화했습니다. 그만큼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 / [anything])에 매핑되고 위쪽 테두리의 픽셀이 ([anything] / 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 이미지 좌표에서 매핑을 얻습니다. Mathematica의 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. 이미지 변환

마지막으로 Mathematica의 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}}]}]]]]

로케이터

이것은 중심 및 반경이 명시 적으로 제공되는 대체 최적화 코드입니다.

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.