도면 벽 검출 구현 - OpenCV.js와 함께

방법이있지·2025년 9월 20일

웹개발

목록 보기
7/19
post-thumbnail

도면 벽 검출

image

<어따놀래>는 사용자가 도면의 사진을 업로드하면 벽을 검출하여 표시하는 기능을 제공합니다.

단 본 프로젝트의 지향점이 AI의 성능 강화보단 전반적인 웹 서비스 구현에 초점을 맞추었기 때문에, 벽 검출 기능에 일부 오차가 있을 수 있습니다.

물론 직접 인공지능 모형을 훈련하거나 하는 식으로 해결할 순 있겠지만, 도면만들기보단 시뮬레이터에 집중해야 할 필요가 있겠다고 생각했습니다. (특히 정글이 AI 부트캠프는 아닌만큼, 선택과 집중을...)

대신 축척 설정 및 벽 추가 / 삭제 등 에디터 기능 역시 구현되어 있으므로, 벽 검출에 오차가 있더라도 사용자가 바로 수정할 수 있습니다.

Hough Transformation

소스 코드

벽 검출에는 공개 컴퓨터 비전 라이브러리인 OpenCV.js의 Hough Transformation을 사용하였습니다.

Hough Transformation은 도형 및 사진에서 선을 검출해 주는 알고리즘입니다. 동작 과정을 차례로 설명해 보겠습니다.

0. 원본 이미지

image

1. 그레이스케일 변환

image
window.cv.cvtColor(srcImg, gray, window.cv.COLOR_RGBA2GRAY);
  • 벽 검출에 불필요한 색상 정보를 없애고 그레이스케일로 변환합니다.

2. 이진화

image
window.cv.threshold(gray,
  binary,
  0,
  255,
  window.cv.THRESH_BINARY_INV + window.cv.THRESH_OTSU
);
  • 실제로는 흑백 이미지도 0부터 255까지의 밝기를 가집니다. Otsu 이진화는 그레이스케일 이미지를 완전한 검은색(0) 또는 흰색(255)로 변환하는 과정입니다.
  • 0부터 255까지 모든 밝기 값의 빈도를 구한 뒤, 빈도의 분산이 최대가 되는 임계값을 구합니다.
  • 분산은 '어두운 그룹과 밝은 그룹의 평균 밝기 차이가 가장 커지도록 이미지를 둘로 나누는 기준 값'을 말합니다.
  • 이후 임계값보다 어두우면 흰색(0, 벽을 나타냄), 밝으면 검은색(255, 벽이 아닌 곳을 나타냄)으로 처리됩니다.

3. 모폴로지 연산 (노이즈 계산)

image
const kernel = window.cv.Mat.ones(3, 3, window.cv.CV_8U);
window.cv.morphologyEx(binary, cleaned, window.cv.MORPH_OPEN, kernel);
  • 모폴로지 연산은 이미지의 점들을 조금씩 늘리거나 줄여서, 작은 얼룩을 없앨 수 있습니다.
  • 선 내 작은 구멍을 메우고 연결해 주는 역할을 수행합니다.
  • 3 x 3 행렬로 주변 9개 점들을 한 번에 보면서 작은 점들을 지우고, 끊어진 선들을 이어줍니다.

4. Canny 엣지 검출을 통한 윤곽선 계산

image
window.cv.Canny(cleaned, edges, canny1, canny2, 3, false);
  • Canny 엣지검출은 이미지에서 가장자리, 즉 윤곽선을 검출하는 알고리즘입니다.
  • Canny 임계값은 벽과 배경 사이 밝기 차이를 측정합니다.
  • canny1(50)보다 낮으면 경계선이 아니라고 판단하고, canny2(150)보다 높으면 확실한 경계선으로 판단합니다. 50과 150 사이의 값은 주변에 확실한 경계선이 있을 때만 경계선으로 인정합니다.

5. Probabilistic Hough를 통한 벽 선분 추출

image
window.cv.HoughLinesP(
  edges,
  lines,
  1,                    // rho: 거리 해상도
  Math.PI / 180,        // theta: 각도 해상도 (1도)
  houghTh,              // 임계값 (80)
  minLen,               // 최소 선분 길이 (30)
  maxGap                // 선분 간 최대 간격 (20)
);
  • Hough Transform을 통해 흩어진 점을을 이어 선분을 검출할 수 있습니다.
  • rho는 선의 위치를 1픽셀 단위로, theta는 선의 기울기를 1도 단위로 세밀하게 측정하여 정확한 직선을 찾아냅니다.
  • houghTh(80)는 몇 개의 점이 일직선 위에 있어야 선으로 인정할지 정하는 값입니다. 80개 이상의 점이 일직선상에 있으면 벽으로 판단합니다.

6. 선분 필터링

image
filterLines(lines, minLength = 80, angleThreshold = 5)
  • 실제 벽으로 보기 어려운 선분을 제거합니다.
  • 너무 짧은 선분(80px 미만)이나, 수직/수평이 아닌 선분 (회전각도가 5도 이상)인 경우 제거됩니다.

7. 평행선 병합

image
mergeParallelLines(lines, maxDistance = 15, angleThreshold = 5)
  • 지금까지의 과정에서는 한 벽에서 여러 개의 선이 검출될 수 있습니다. 따라서 평행한 선을 하나의 선으로 병합할 필요가 있습니다.
  • 위 사진의 분홍색 선은, 원래 선들 중 병합된 선을 의미합니다.
  • angleThreshold: 5도 이내의 각도 차이만 평행선으로 인정합니다.
  • maxDistance: 15px 이내 거리의 선만 병합 대상입니다.
  • 겹침 구간이 10% 이상인 선분만 병합하여, 정확도를 높입니다.
profile
뭔가 만드는 걸 좋아하는 개발자 지망생입니다. 프로야구단 LG 트윈스를 응원하고 있습니다.

0개의 댓글