OpenCV와 Python을 활용한 명함 이미지 보정 예제
전체 코드
import sys
import os
import numpy as np
import cv2
img_path = os.path.join(os.path.dirname(__file__), 'namecard.jpg')
src = cv2.imdecode(np.fromfile(img_path, dtype=np.uint8), cv2.IMREAD_COLOR)
if src is None:
print('Image load failed!')
sys.exit()
w, h = 720, 400
srcQuad = np.array([
[222, 95],
[622, 178],
[547, 416],
[145, 317]
], np.float32)
dstQuad = np.array([
[0, 0],
[w - 1, 0],
[w - 1, h - 1],
[0, h - 1]
], np.float32)
pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)
dst = cv2.warpPerspective(src, pers, (w, h))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
cv2.imread() 대신 cv2.imdecode()를 사용한 이유
cv2.imread()
cv2.imread()는 파일 경로를 직접 받아 이미지를 읽는 가장 일반적인 방법이다.
내부적으로 파일을 열고 이미지 디코딩까지 한 번에 수행한다.
다만 다음과 같은 한계가 있다.
- 한글 또는 유니코드 경로에서 이미지 로드에 실패할 수 있다.
- 파일 경로 기반이므로 메모리에 올라온 데이터는 직접 처리할 수 없다.
cv2.imdecode()
cv2.imdecode()는 바이트(Byte) 형태의 데이터를 이미지로 디코딩하는 함수이다.
즉, 파일 자체가 아니라 메모리에 올라온 데이터를 처리할 때 사용한다.
예를 들어 다음과 같은 경우에 활용할 수 있다.
- np.fromfile()로 읽어온 파일 데이터
- 네트워크로 전송받은 이미지 데이터
- DB 또는 API에서 받은 이미지 데이터
특히 Windows 환경에서 발생하는 한글 경로 문제를 우회할 수 있다는 장점이 있다.
현재 코드에서는 다음 과정으로 이미지를 읽는다.
np.fromfile(img_path, dtype=np.uint8)
위 코드는 이미지 파일을 바이트 형태로 메모리에 읽어온다.
그 후 cv2.imdecode()가 해당 데이터를 실제 이미지 형태로 디코딩한다.
즉 흐름은 다음과 같다.
파일 → 바이트 데이터 → 메모리 로드 → 이미지 디코딩
바이트 데이터(Byte Data)와 메모리 데이터(Memory Data)
바이트 데이터(Byte Data)
컴퓨터에서 데이터를 저장하는 가장 기본 단위는 Byte이다.
이미지, 영상, 텍스트 파일 모두 결국은 바이트들의 집합으로 이루어져 있다.
즉, 바이트 데이터는 파일 내용 그 자체라고 볼 수 있다.
예시:
FF D8 FF E0 ...
이처럼 이미지 파일도 실제로는 수많은 바이트 값으로 저장된다.
메모리 데이터(Memory Data)
메모리 데이터는 RAM(메모리)에 올라와 있는 데이터를 의미한다.
파일이 디스크에 저장된 상태라면,
메모리 데이터는 프로그램 실행 중 RAM에 적재되어 실제로 사용되는 상태라고 이해하면 된다.
- 바이트 데이터 → 데이터의 실제 내용
- 메모리 데이터 → 데이터가 올라와 있는 위치(RAM)
즉,
바이트 데이터는 “데이터의 형태”이고,
메모리 데이터는 “데이터가 존재하는 위치와 상태”라고 이해하면 된다.


해당 코드의 가장 큰 단점은
명함의 꼭짓점 좌표를 사용자가 직접 입력해야 한다는 점이다.
즉, 이미지가 바뀌거나 촬영 위치와 각도가 달라질 때마다
좌표를 다시 수정해야 한다는 문제가 있다.
좌표지정 원근변환 예제
import sys
import os
import numpy as np
import cv2
img_path = os.path.join(os.path.dirname(__file__), 'namecard.jpg')
src = cv2.imdecode(np.fromfile(img_path, dtype=np.uint8), cv2.IMREAD_COLOR)
if src is None:
print('Image load failed!')
sys.exit()
w, h = 720, 400
clicks = []
def on_mouse(event, x, y, _flags, _param):
if event == cv2.EVENT_LBUTTONDOWN and len(clicks) < 4:
# 사용자가 클릭한 위치를 리스트에 저장한다.
clicks.append([x, y])
# 클릭한 위치를 화면에 표시하기위해 빨간 원을 그린다.
cv2.circle(display, (x, y), 6, (0, 0, 255), -1)
# 번호 출력
cv2.putText(display, str(len(clicks)), (x + 8, y - 8),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
cv2.imshow('src', display)
if len(clicks) == 4:
print('선택 좌표:', clicks)
# 클릭 좌표를 Numpy배열로 변환한다.
# getPerspectiveTransform()은 float32 타입 좌표를 요구하기 때문에 float32 타입으로 변경해준다.
srcQuad = np.array(clicks, dtype=np.float32)
dstQuad = np.array([[0, 0], [w-1, 0], [w-1, h-1], [0, h-1]], np.float32)
pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)
dst = cv2.warpPerspective(src, pers, (w, h))
cv2.imshow('dst', dst)
display = src.copy()
print('좌상 → 우상 → 우하 → 좌하 순서로 4번 클릭하세요.')
cv2.imshow('src', display)
cv2.setMouseCallback('src', on_mouse)
cv2.waitKey()
cv2.destroyAllWindows()
전체 코드 흐름
1. 이미지 로드
2. 마우스 클릭 이벤트 등록
3. 사용자가 명함의 꼭짓점 4개 선택
4. 클릭 좌표 저장
5. 원근 변환 행렬 계산
6. 명함 이미지를 정면 형태로 변환
7. 결과 이미지 출력
cv2.EVENT_LBUTTONDOWN
cv2.EVENT_LBUTTONDOWN
마우스 이벤트 종류 중 하나로,
사용자가 마우스의 왼쪽 버튼을 클릭했을 때 발생한다.
현재 코드에서는:
if event == cv2.EVENT_LBUTTONDOWN:
조건을 통해 사용자가 이미지를 클릭했는지 확인한다.
클릭이 발생하면:
- 클릭 좌표 (x, y)를 저장하고
- 화면에 클릭 위치를 표시한다.
cv2.getPerspectiveTransform()
pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)
입력 좌표를 출력 좌표로 변환하기 위한
원근 변환 행렬(Perspective Transform Matrix)을 계산하는 함수이다.
기울어진 사각형을 반듯한 직사각형으로 어떻게 변환할 것인지 계산하는 과정
이라고 볼 수 있다.
srcQuad
원본 이미지에서 선택한 좌표이다.
즉, 사용자가 클릭한 명함의 네 꼭짓점 좌표를 의미한다.
예시:
srcQuad = np.array([
[222, 95],
[622, 178],
[547, 416],
[145, 317]
], np.float32)
dstQuad
변환 후 목표 좌표이다.
즉, 보정된 결과 이미지에서의 직사각형 좌표를 의미한다.
예시:
dstQuad = np.array([
[0, 0],
[w-1, 0],
[w-1, h-1],
[0, h-1]
], np.float32)
반환값 pers
반환값은 3×3 크기의 변환 행렬(Matrix)이다.
이 행렬을 이용해 이미지의 각 픽셀 위치를 새롭게 계산한다.
즉:
기울어진 명함
→
정면 형태의 명함
으로 변환하기 위한 수학적 규칙이라고 이해하면 된다.
cv2.warpPerspective()
dst = cv2.warpPerspective(src, pers, (w, h))
실제 원근 변환을 수행하는 함수이다.
앞에서 계산한 변환 행렬 pers를 이용하여
이미지를 새로운 형태로 변환한다.
즉, 기울어진 명함 이미지를 정면 형태로 보정한다.
매개변수 설명
src
src
원본 이미지이다.
pers
pers
cv2.getPerspectiveTransform()에서 계산된 변환 행렬이다.
(w, h)
(w, h)
결과 이미지의 크기를 의미한다.
예시:
w = 720
h = 400
이라면:
- 가로 720px
- 세로 400px
크기의 결과 이미지가 생성된다.
cv2.setMouseCallback()
cv2.setMouseCallback('src', on_mouse)
특정 창(window)에 마우스 이벤트 함수를 등록하는 함수이다.
즉:
'src' 창에서 마우스 이벤트가 발생하면
on_mouse() 함수를 실행하라
는 의미이다.
사용자가 이미지를 클릭하면 OpenCV가 자동으로 이벤트를 감지하고,
등록된 on_mouse() 함수를 호출한다.
이를 통해:
- 클릭 좌표 저장
- 클릭 위치 표시
- 원근 변환 수행
등의 작업을 처리할 수 있다.


'Programing' 카테고리의 다른 글
| [error] name '__file__' is not defined (0) | 2026.05.08 |
|---|---|
| Pandas 기초 퀴즈 (0) | 2026.05.07 |
| 영상처리 입문 (0) | 2026.05.06 |
| SQLAlchemy Column vs Mapped (0) | 2026.04.30 |
| Python - Class (0) | 2026.04.17 |