대상: OpenCV로 이미지 처리나 ROI(관심영역) 지정, 픽셀 좌표 계산을 자주 하는 개발자
환경: Ubuntu 20.04 / 22.04, Python 3.x 또는 C++ 기반 OpenCV 프로젝트
1. 문제/주제 요약
OpenCV를 사용하다 보면 (x, y) 좌표와 (row, col) 인덱스 개념이 자주 헷갈린다.
특히 cv::circle(), cv::rectangle(), img[y][x] 접근 등이 혼용되어 있을 때, 좌표가 뒤집히거나 픽셀이 엉뚱한 위치에 표시되는 문제가 발생한다.
이 글에서는 OpenCV의 좌표계 기준과 행렬 인덱싱의 차이를 명확히 정리한다.
2. 원인/배경 설명
OpenCV의 이미지는 행렬(Mat) 형태로 저장되며, 행(row) 과 열(col) 의 개념을 가진다.
하지만 그래픽 함수(cv2.line, cv2.circle)는 수학적 좌표계(x, y) 를 사용한다.
즉, 다음 두 개념이 서로 다르다:
| 구분 | 개념 | 의미 | OpenCV에서 주로 쓰이는 곳 |
|---|---|---|---|
| 행렬 인덱스 | (row, col) | 이미지 배열의 위치 | img[row][col], img[y][x] |
| 좌표계 | (x, y) | 수평/수직 거리 (픽셀 좌표) | cv2.circle(img, (x, y)), cv2.putText() |
즉,
- 행렬(row, col) 은
y가 먼저,x가 나중 - 좌표(x, y) 는
x가 먼저,y가 나중
3. 실제 예시로 비교하기
(1) 이미지 픽셀 접근
import cv2
img = cv2.imread('image.jpg')
# 행렬 접근 방식
b, g, r = img[100, 200] # (row=100, col=200)
# 여기서 100은 y좌표, 200은 x좌표와 대응된다.
print(f"픽셀 위치: (x={200}, y={100}), 색상={r,g,b}")
즉, img[y, x] 로 접근해야 원하는 픽셀을 가져올 수 있다.
(2) OpenCV 도형 함수
도형을 그릴 때는 (x, y) 순서로 입력한다.
cv2.circle(img, (200, 100), 5, (0, 0, 255), -1)
cv2.putText(img, "Point (200,100)", (200, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255))
주의:
cv2.circle()은(x, y)좌표,
하지만img[y, x]는 행렬 접근이므로 순서가 반대다.
(3) ROI(Region of Interest) 지정
# ROI: y축(세로), x축(가로)
roi = img[100:200, 150:300] # y:100~199, x:150~299
→ 즉, ROI의 첫 번째 인덱스는 세로(y), 두 번째 인덱스는 가로(x)
(4) 좌표계를 시각적으로 정리하면:
(0,0) ----------------------> x (col)
|
|
|
v
y (row)
즉,
- x는 오른쪽으로 증가
- y는 아래쪽으로 증가
- OpenCV의 원점(0,0)은 왼쪽 상단
4. 추가 팁 / 자주 하는 실수
| 실수 | 잘못된 예 | 올바른 예 |
|---|---|---|
| 점 표시가 엉뚱한 위치에 그려짐 | cv2.circle(img, (y, x), 5, ...) | cv2.circle(img, (x, y), 5, ...) |
| ROI 범위를 반대로 설정 | img[x1:x2, y1:y2] | img[y1:y2, x1:x2] |
| 픽셀 접근 시 좌표계 기준 혼동 | img[x, y] | img[y, x] |
💡 팁:
- 이미지 배열(
img[][]) 접근 시:(y, x) - 도형, 텍스트, 좌표 기반 함수 사용 시:
(x, y) - numpy 기반 연산을 할 때는 항상
img.shape = (height, width, channels)로 생각하자.
5. 정리
- OpenCV는 행렬 기반 구조(Mat) 이므로 인덱싱은
(row, col)→(y, x)순서다. - 하지만 그래픽 함수와 좌표계는
(x, y)로 사용한다. - 헷갈릴 때는 항상
img.shape와cv2.circle의 인자 순서를 비교해보면 혼동을 줄일 수 있다.