printf("ho_tari\n");
12일차 본문
2025.04.29
이미지 변형 - 흐림
흐림 효과 (Blur)
- 블러링(Blurring) 또는 스무딩(Smoothing)이라 불림
- 영상이나 이미지를 번지게 하며, 해당 픽셀의 주변 값들과 비교하고 계산해서 픽셀들의 색상을 재조정함
- 단순히 이미지를 흐리게 만드는 것뿐만 아니라 노이즈를 제거해서 연산 시 계산을 빠르고 정확하게 수행하는 데 도움을 줌
- 노이즈를 줄이거나 외부 영향을 최소화하는 데 사용
- 의도적으로 이미지를 흐리게 하는 이유는 보이는 정보를 조정함으로써 외곽선 정보를 보다 단순하게 처리함
cv2.blur()
- 이것은 단순 평균 블러링을 수행합니다.
- 지정된 커널 크기 내의 모든 픽셀 값의 평균을 계산하여 이미지를 흐리게 만듭니다.
- 결과적으로 동일한 가중치로 주변 픽셀의 값을 평균화합니다.
`dst = cv2.blur(src, ksize, anchor, borderType)` :입력 이미지(src)를 커널 크기(ksize), 고정점(anchor), 테두리 외삽법(borderType)으로 흐림 효과를 적용한 결과 이미지(dst)를 반환
- ksize : 커널(kernel) 크기-이미지에서 (x, y)의 픽셀과 설정한 크기의 사각형 커널이며 커널 크기가 클수록 더 흐려짐
- anchor :(-1, -1) 커널의 중심을 의미함, 기본값: 커널 내에서의 앵커 포인트(커널을 이미지에 적용할 때 기준이 되는 특정 픽셀 위치)
- borderType : 테두리 외삽법(Border Extrapolation) - 컨벌루션을 적용할 때, 이미지 가장자리 부분의 처리 방식
- borderType=cv2.BORDER_DEFAULT : 기본 테두리 유형 사용
가우시안 블러
`blurred_image = cv2.GaussianBlur(source_image, (kernel_width, kernel_height), sigmaX, sigmaY)`
- 이미지를 흐리게 하면서도 노이즈도 제거함. 이렇게 하면 이미지 연산할때 보다 빠르게 정확하게 처리할 수 있음 = > 노이즈 제거, 이미지 부드럽게 처리, 엣지 검출을 위한 사전 처리 단계 등 다양한 목적으로 활용
- 가우시안 함수를 기반으로 한 가중치를 사용하여 블러링을 수행합니다.
- 커널의 중심에 가까운 픽셀은 더 높은 가중치를 가지며, 멀리 떨어진 픽셀은 더 낮은 가중치를 가집니다.
- 커널의 크기는 블러링의 정도를 결정
- 큰 커널은 이미지를 더 흐리게 만들며,
- 작은 커널은 덜 흐리게 만듬
- sigmaX : 가우시안 블러에서 X 방향의 표준 편차는 블러의 정도와 연관이 있음
- 큰 표준 편차는 이미지를 더 흐리게 만들며,
- 작은 표준 편차는 덜 흐리게 만듬
- sigmaY : Y 방향(세로) 가우시안 커널 표준편차 (기본값은 0)
cv2.blur() vs cv2.GaussianBlur() vs cv2.medianBlur()
- `cv2.blur()`평균블러링링: 단순히 주변 픽셀 평균 계산- 더 단순하고 빠르게 이미지를 흐리게 만들 수 있습니다. 특정 애플리케이션에서는 이것이 더 적합할 수 있음
- `cv2.GaussianBlur()`:가중 평균 (중앙 픽셀에 더 많은 가중치) - 이미지의 노이즈를 줄이는 데 특히 효과적입니다. 가우시안 블러는 노이즈 제거와 동시에 이미지의 경계를 부드럽게 만들어 주기 때문에 많은 컴퓨터 비전 작업에서 선호됨.
- `cv2.medianBlur()`: 미디언 블러링 (cv2.medianBlur) - 주변 픽셀의 중앙값을 사용, 노이즈 제거에 최고(특히 소금-후추 노이즈 제거 효과 뛰어남)
샤프닝 (Sharpening)
- 영상을 날카롭게 만드는 처리
- 영상을 선명하게 할 때 사용
- 중심 화소값과 인접 화소값의 차이를 더 크게 만듬
- cv2.filter2D : OpenCV 라이브러리에서 제공하는 함수로, 사용자가 정의한 커널(또는 필터)를 이미지에 적용하여 2차원 필터링을 수행하는 기능을 제공
- `dst = cv2.filter2D(src, ddepth, kernel, anchor, delta, borderType)`
- src: 입력 이미지입니다. 보통 numpy 배열로 표현됩니다.
- ddepth: 결과 이미지의 깊이(비트 단위)입니다. -1로 지정하면 입력 이미지와 동일한 깊이를 사용합니다.
- kernel: 필터링에 사용될 커널입니다. numpy 배열로 정의되며, 일반적으로 부동소수점 값으로 구성됩니다.
- anchor: 선택적으로 커널의 중심을 지정하는 점입니다. 기본값은 (-1, -1)로, 커널의 중심을 사용합니다.
- delta: 필터링된 픽셀에 추가적으로 더해질 값. 기본값은 0입니다.
- borderType: 이미지 가장자리 픽셀을 확장하는 방식을 결정합니다. 예를 들어, cv2.BORDER_CONSTANT, cv2.BORDER_REFLECT, cv2.- BORDER_WRAP 등이 있습니다.
이미지 검출 (경계선)
Canny Edge Detection
- 이미지에서 **윤곽선(에지)**를 똑똑하게 찾아주는 아주 중요한 기법
- 컬러이미지에 적용할 경우 내부적으로 회색조로 변경한 후 에지 검출을 수행함.
- Canny Edge Detection 4단계
- 1️⃣ Noise Reduction (노이즈 제거) : 잡음 때문에 잘못된 엣지를 검출하는 걸 막기 위해 **Gaussian Blur (가우시안 블러링)**을 적용해서 부드럽게 만들기
```python
blur = cv2.GaussianBlur(img, (5,5), 1.4)
```
- 2️⃣ Finding Intensity Gradient (기울기(gradient) 계산) : 밝기가 얼마나 급격하게 변하는지 (밝기 차이) 계산. 변하는 정도(기울기)와 방향(어디로 밝아지는지)을 알아내기 위해 OpenCV 내부에서는 Sobel 연산 사용
```python
Gx = Sobel(blur, 방향=x) # 수평 기울기
Gy = Sobel(blur, 방향=y) # 수직 기울기
magnitude = sqrt(Gx^2 + Gy^2) # 전체 기울기 크기
direction = atan2(Gy, Gx) # 기울기 방향
```
- 3️⃣ Non-Maximum Suppression (비최대 억제) : 기울기(gradient)가 가장 강한 부분만 살리고, 주변 약한 부분은 지움 => 가장 뾰족한(최고점)만 남겨서 엣지를 가늘게 만들어주는 단계 (최대 크기의 픽셀만 골라내서 에지 픽셀로 설정)
- 4️⃣ Hysteresis Thresholding (이력 기반 문턱값 처리) : 모든 에지 후보들이 실제 에지인지 아닌지를 결정
- 조명이나 다른 변수의 영향으로 인해 영상의 그래디언트가 미세하게 변화할 수 있기때문에 픽셀 값이 임계점보다 커지거나 낮아질 수 있는데, 이를 방지하기 위해 두 개의 임계값을 이용함.
- 강한 엣지는 무조건 엣지로 채택하고, 약한 엣지는 강한 엣지와 연결된 경우에만 엣지로 인정 => 진짜 엣지와 연결된 부분만 살려서 깔끔하게 정리. OpenCV에서는 두 개의 문턱값을 설정
```python
edges = cv2.Canny(img, threshold1=50, threshold2=150)
```
- `cv2.Canny(img, threshold1, threshold2)`
- threshold1 (하위 임계값, minVal): 이 값 이하의 그레이디언트(명암 변화율)를 가진 픽셀은 에지가 아니라고 간주함(에지로 간주되기 위한 최소한의 명암 변화량)
- threshold2 (상위 임계값, maxVal): 이 값 이상의 그레이디언트를 가진 픽셀은 강한 에지로 간주되며, threshold1과 threshold2 사이의 그레이디언트를 가진 픽셀은 연결된 강한 에지가 존재할 경우에만 에지로 간주함
이미지 검출 - 윤곽선
1. 컨투어(Contour) : 경계선을 연결한 선
- 컨투어(우리말로 등고선, 윤곽선, 외곽선 등으로 번역)s는 지도에서 지형의 높이가 같은 영역을 하나의 선으로 표시하는 용도로 많이 사용됨
- 영상에서는 같은 색상이나 밝기의 연속된 점을 찾아 잇는 곡선을 찾아내면 모양 분석과 객체 인식에 사용할 수 있음
- `contour, hierarchy = cv2.findCountours(src, mode, method[, contours, hierarchy, offset])[-2:]`
- src : 입력 이미지, 바이너리 스케일, 검은색 배경 흰색 전경
mode : 컨투어 제공 방식 선택
- cv2.RETR_EXTERNAL : 가장 바깥쪽 라인만 제공
- cv2.RETR_LIST : 모든 라인을 계층 없이 제공
- cv2.RETR_CCOMP : 모든 라인을 2계층으로 제공
- cv2.RETR_TREE : 모든 라인의 모든 계층 정보를 트리 구조로 제공
- method : 근사 값 방식 선택
- cv2.CHAIN_APPROX_NONE : 근사 계산하지 않고 모든 좌표 제공
- cv2.CHAIN_APPROX_SIMPLE : 컨투어 꼭짓점 좌표만 제공
- cv2.CHAIN_APPROX_TC89_L1 : Teh-Chin 알고리즘으로 좌표 개수 축소
- cv2.CHAIN_APPROX_TC89_KCOS : Teh-Chin 알고리즘으로 좌표 개수 축소
- contours : 검출한 건투어 좌표, 파이썬 리스트
- hierarchy : 컨투어 계층 정보
- `cv2.drawContours(img, contours, contourIdx, color, thickness)`
- img : 입력 영상
- contours : 그림 그릴 컨투어 배열
- contourIdx : 그림 그릴 컨투어 인덱스, -1 : 모든 컨투어 표시
- color : 색상
- thickness : 선 두께, 0:채우기
2. 윤곽선 검출 : findContours()
- 원본이미지를 직접 수정하는 함수임. 따라서 원본이미지를 보존하기위해 복사해서 사용
3. 윤곽선 그리기 : drawContours()
윤곽선 찾을 때 근사치 방법
- cv2.findContours() : 이진화 이미지에서 윤곽선(컨투어)를 검색
- `contours, hierarchy = cv2.findContours(binary, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)`
- contours : 이미지에서 경계를 찾고, 경계 좌표를 contours와 같은 형태로 반환합니다.
- 윤곽선, 계층구조 = cv2.findContours(이진화 이미지, 검색 방법, 근사화 방법)
- 검색 방법
- cv2.RETR_EXTERNAL : 외곽 윤곽선만 검출하며, 계층 구조를 구성하지 않습니다.
- cv2.RETR_LIST : 모든 윤곽선을 검출하며, 계층 구조를 구성하지 않습니다.
- cv2.RETR_CCOMP : 모든 윤곽선을 검출하며, 계층 구조는 2단계로 구성합니다.
- cv2.RETR_TREE : 모든 윤곽선을 검출하며, 계층 구조를 모두 형성합니다. (Tree 구조)
- 근사화 방법
- cv2.CHAIN_APPROX_NONE : 윤곽점들의 모든 점을 반환합니다.
- cv2.CHAIN_APPROX_SIMPLE : 윤곽점들 단순화 수평, 수직 및 대각선 요소를 압축하고 끝점만 남겨 둡니다.
- cv2.CHAIN_APPROX_TC89_L1 : 프리먼 체인 코드에서의 윤곽선으로 적용합니다.
- cv2.CHAIN_APPROX_TC89_KCOS : 프리먼 체인 코드에서의 윤곽선으로 적용합니다.
- contours : Numpy 구조의 배열로 검출된 윤곽선의 지점들이 담겨있음
- hierarchy : 윤곽선의 계층 구조를 의미. 윤곽선의 포함 관계 여부 나타냄 (외곽/내곽/같은 게층구조)윤곽선에 해당하는 속정 정보 포함함
윤곽선 찾기 모드
- cv2.RETR_EXTERNAL : 가장 외곽의 윤곽선만 찾음
- cv2.RETR_LIST : 모든 윤곽선 찾음 (계층 정보 없음)
- cv2.RETR_TREE : 모든 윤곽선 찾음 (계층 정보를 트리 구조로 생성)
- cv2.drawContours() : 검출된 윤곽선 그리기
- `cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]] ) -> image`
- cv2.drawContours(이미지, [윤곽선], 윤곽선 인덱스, (B, G, R), 두께, 선형타입)
- image : 윤곽선을 그릴 대상 이미지 - 빈 캔버스에 그림
- 윤곽선 : 검출된 윤곽선들이 저장된 Numpy 배열(좌표 리스트)
- 윤곽선 인덱스: 검출된 윤곽선 배열에서 몇 번째 인덱스의 윤곽선을 그릴지를 의미합니다.
- 윤곽선 인덱스를 0으로 사용할 경우 0 번째 인덱스의 윤곽선을 그리게 됩니다. 하지만, 윤곽선 인수를 대괄호로 다시 묶을 경우, 0 번째 인덱스가 최댓값인 배열로 변경됩니다.
- 동일한 방식으로 [윤곽선], 0과 윤곽선, -1은 동일한 의미를 갖습니다. (-1은 윤곽선 배열 모두를 의미)
- 예시 [ 2 -1 1 -1] : [다음 윤곽선, 이전 윤곽선, 내곽 윤곽선, 외곽 윤곽선]에 대한 인덱스 정보를 포함
- 인덱스 0의 윤곽선의 다음 윤곽선은 인덱스 2의 윤곽선을 의미하며 이전 윤곽선은 존재하지 않다는 것을 의미
- 내곽 윤곽선은 인덱스 1에 해당하는 윤곽선을 자식 윤곽선으로 두고 있다는 의미입니다.즉, 인덱스 0 윤곽선 내부에 인덱스 1의 윤곽선이 포함되어 있습니다.
- 외곽 윤곽선은 -1의 값을 갖고 있으므로 외곽 윤곽선은 존재하지 않습니다.
경계 사각형
- 윤곽선의 경계면을 둘러싸는 사각형 : boundingRect()
면적
- 특정 면적 이상의 윤곽선만 찾고 싶을 때 사용 : contourArea()
HOG (Histogram of Oriented Gradient)
- 주로 **사람 인식(Human Detection)**과 보행자 검출을 하기 위해 널리 사용되는 기법 중 하나로 이미지의 지역적 형태(윤곽선, 경계)를 잡아내는 특성(Feature) 추출 알고리즘
- 그래디언트 방향 히스토그램을 의미하며 사람이 서 있는 영상에서 그래디언트를 구하고, 그래디언트의 크기와 방향 성분을 이용하여 사람이 서 있는 형태에 대한 특징 벡터를 정의 => 모양(shape) 정보에 강함, 배경 노이즈에 강인
- 이미지를 일정한 셀로 나누고, 각 셀에서 그래디언트 방향의 히스토그램을 계산
- 1)대상 영역을 일정 크기의 셀로 분할
- 2)각 셀마다 edge 픽셀(gradient magnitude가 일정 값 이상인 픽셀)들의 방향에 대한 히스토그램을 구한 후
- 3)이들 히스토그램 bin 값들을 일렬로 연결한 벡터
- 템플릿 매칭과 히스토그램 매칭의 중간 단계에 있는 매칭 방법으로 볼 수 있으며 블록 단위로는 기하학적 정보를 유지하되, 각 블록 내부에서는 히스토그램을 사용함으로써 로컬한 변화에는 어느정도 강인한 특성을 가짐
- 1) 입력영상에서 부분영상 추출
- 2) 크기 정규화 : 64X128
- 3) 64X128영상의 gradient 계산하여 방향과 크기 성분 파악
- 4) 8*8 크기의 셀분할
- 5) 방향 시스토그램의 bin 개수 = 9 => 360도를 9개 구간으로 나옴
> 8*8 셀 4개를 하나의 블록을 지정 ( 블록 하나의 크기는 16X16 => 각 블록의 히스토그램 빈 개수는 4X9=36개)
- `hog(gray, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(2, 2), visualize=True, block_norm='L2-Hys')
- orientations=9 : 방향(bin) 개수 (360도를 9등분)
- pixels_per_cell=(8, 8) : 한 셀(cell) 크기
- cells_per_block=(2, 2) : 블록(block) 내 셀 개수 (정규화 단위)
- visualize=True : HOG 시각화 결과도 리턴
- block_norm='L2-Hys' :블록 정규화 방법 (L2-Hysteresis) => 블록(block) 안의 HOG 히스토그램 벡터를
"너무 작거나 너무 큰 값을 정리(normalize)해서 비교가 잘 되게" 하는 과정
-`cv2.HOGDescriptor.detectMultiScale(img, hitThreshold=None, winStride=None, padding=None, scale=None, finalThreshold=None, useMeanshiftGrouping=None) -> foundLocations, foundWeights`
- img: 입력 영상. cv2.CV_8UC1 또는 cv2.CV_8UC3.
- hitThreshold: 특징 벡터와 SVM 분류 평면까지의 거리에 대한 임계값
- winStride: 셀 윈도우 이동 크기. (0, 0) 지정 시 셀 크기와 같게 설정.
- padding: 패딩 크기
- scale: 검색 윈도우 크기 확대 비율. 기본값은 1.05.
- finalThreshold: 검출 결정을 위한 임계값
- useMeanshiftGrouping: 겹쳐진 검색 윈도우를 합치는 방법 지정 플래그
- foundLocations: (출력) 검출된 사각형 영역 정보
- foundWeights: (출력) 검출된 사각형 영역에 대한 신뢰도
SIFT (Scale Invariant Feature Transform)
- 크기(Scale), 회전(Rotation), 조명 변화에 강인한 특징점(keypoint)을 추출하는 알고리즘
- 이미지에서 눈에 띄는 지점(코너, 엣지 등)을 찾고, 해당 위치에서 128차원 디스크립터를 계산함
- 이미지 매칭, 객체 인식 등에서 사용됨
- SIFT는 영상에서 코너점 등 식별이 용이한 특징점들을 선택한 후에 각 특징점을 중심으로 한 로컬 패치(local patch)에 대해 아래 그림과 같은 특징 벡터를 추출한 것을 말함.
- 1)특징점 주변의 영상패치를 4 x 4 블록으로 나누고
- 2)각 블록에 속한 픽셀들의 gradient 방향과 크기에 대한 히스토그램을 구한 후 이 히스토그램 bin 값들을 일렬로 쭉 연결한 128차원 벡터
Haar-like Feature (Haar Cascade)
- 사각형 박스를 기준으로 밝기 차이를 비교하는 간단한 특징
- 빠르고 효율적이지만 딥러닝에 비해 정밀도는 낮음
- 주로 얼굴 검출에 사용됨 (Haar Cascade Classifier)
# OpenCV를 사용하여 실시간으로 웹캠에서 움직임을 감지하고, 움직임이 일정 기준 이상 발생하면 녹화를 시작하는 프로그램
################################################################################################ # 미션 1:
# - 웹캠에서 실시간으로 움직임을 감지하여 자동으로 녹화를 시작하세요.
# - 움직임이 감지되면 최소 5초 동안 동영상을 저장하고, 움직임이 멈추면 녹화를 종료하세요.
# - 각 프레임에 현재 시간을 표시하여 저장된 영상에 타임스탬프를 추가하세요.
# - 결과:
# - 움직임 감지 결과(diff)와 현재 시간을 표시한 프레임을 화면에 출력.
# - 움직임이 감지되면 "Recording..." 메시지 출력.
# - 녹화된 영상 파일은 'Capture' 폴더에 저장 (예: 녹화_YYYY_MM_DD_HH_MM_SS.avi).
# - 조건:
# - 연속된 3개의 프레임을 비교하여 움직임 감지 (absdiff 사용).
# - 움직임 픽셀 수가 특정 임계값(diff_min)을 초과하면 움직임으로 간주.
# - 타임스탬프는 영상 상단에 표시 (PIL.ImageFont 사용).
# - 주의:
# - 녹화 파일명에 타임스탬프를 포함하여 중복되지 않도록 설정.
################################################################################################
################################################################################################
# 미션 2 :
# - 이미지에서 파란색, 노란색, 초록색 네모 상자를 감지하세요.
# - HSV 색상 공간에서 각 색상에 해당하는 범위를 지정하고 마스크를 생성하여 윤곽선을 감지하세요.
# - 각 윤곽선에 대해 최소 바운딩 박스를 계산하고, 상자의 중심 좌표와 회전 각도를 구하세요.
# - 감지된 상자 주변에 외곽선을 색상별로 그리세요:
# - 파란색 상자: 파란 외곽선
# - 노란색 상자: 노란 외곽선
# - 초록색 상자: 초록 외곽선
# - 결과:
# - 각 색상의 상자 중심 좌표 및 각도를 콘솔에 출력.
# - 이미지에 각 상자의 외곽선을 그려 시각적으로 결과를 확인.
# - 조건:
# - HSV 색상 범위를 정확히 지정하여 노이즈를 최소화.
# - 윤곽선의 면적 기준(100 픽셀)으로 노이즈 필터링.
# - 박스는 `cv2.minAreaRect`와 `cv2.boxPoints`를 이용해 그리기.
# - 주의:
# - 여러 색상을 한꺼번에 처리하여 코드의 효율성을 유지.
# - 각 색상별로 구분된 결과를 명확히 표시.
################################################################################################
< OpenCV 얼굴인식 프로젝트1 >
## Step1 : 데이터 수집하기
- 웹캠에서 얼굴 부분만 검출하여 사진 100장 찍어 폴더에 저장하기
1) cv2.CascadeClassifier() 객체 선언하기
2) cv2.VideoCapture(0) 객체 선언하기
3) 웹캠이 열려있으면
- frame 을 읽어서
- 회색조로 변경
- detectMultiScale() 사용하여 얼굴 감지하기
- 감지된 얼굴에 대해 사이즈 줄여 지정한 폴더에 구분자를 붙여 이미지로 저장하기(cv2.imwrite())
- 화면에는 카운트 숫자 표기하기
- 'q'로 종료하거나 count 가 100일때 종료하기
## Step2 : 모델 학습하기
- 저장된 얼굴 이미지를 읽어서 모델 학습하기
1) 저장된 폴더에서 이미지를 읽어(cv2.imread() ) 회색조로 저장하기
2) 100장의 이미지를 리스트에 학습할 데이터(Training_Data) 와 Labels을 append 하여 각각 저장하기
3) 모델 객체 생성하기 : model = cv2.face.LBPHFaceRecognizer_create()
4) 모델 학습하기 : model.train(Training_Data, Labels)
5) 훈련된 모델 파일로 저장하기 :model.write('face_model.yml')
### 주의!
- cv2.face.LBPHFaceRecognizer_create()
을 이용해 모델을 학습시키기 할때 다음 에러 발생 시 설치하기
> !pip install --user opencv-python-headless==4.8.1.78
> !pip install --user opencv-contrib-python==4.8.1.78
> !python -m pip install --upgrade pip
> !pip3 install scikit-build
> !pip install cmake
## Step3 : 얼굴 인식하기
- 분류기를 사용하여 웹캠에 비친 얼굴 중 학습한 사용자 인식하기
1) cv2.CascadeClassifier() 객체 선언하기
2) cv2.VideoCapture(0) 객체 선언하기
3) 웹캠이 열려있으면
- frame 을 읽어서
- 회색조로 변경
- detectMultiScale() 사용하여 얼굴 감지하기
- 얼굴이 검출된 경우
- 회색조로 변경하고
- 모델 예측하기 : model.predict()
- 예측 결과 신뢰도로가 75 이상인경우 화면에 "ACCESS GRANTED"
아니면 "ACCESS DENIED"
- 'q'로 종료
### 주의! : step3 에서 'AttributeError: module 'cv2.cv2' has no attribute 'face' 발생시
> !pip install --user opencv-contrib-python==4.6.0.66