본문 바로가기
프로그램/SKT FLY AI

SKT FLY AI : 27일차

by hsloth 2023. 8. 1.

Mask 이미지

  • 이미지 전처리를 수행하거나, 딥러닝을 위한 Segmentation을 수행할 때, Mask라는 것을 사용할 때가 있다.
  • 원본 이미지에서 사용자가 관심이 있는 영역(ROI)은 255 픽셀 값으로 채우고, 그 이외의 영역은 0픽셀 값으로 채워서 만든 흑백 이미지를 Binary Mask라고 한다.
  • RGB Mask도 있다.

이미지 합성

  • OpenCV에서는 원본 이미지, Mask 이미지를 활용하여 합성을 할 수 있다.
  • 08.bitwise_overlap.py
import numpy as np, cv2

image = cv2.imread("images/bit_test.jpg", cv2.IMREAD_COLOR)  # 원본 영상 읽기

logo = cv2.imread("images/logo.jpg", cv2.IMREAD_COLOR)  # 로고 영상 읽기

# threshold : 이진화처리를 할 때, 기준이 되는 임계값을 결정할 때 사용하는 함수
# 이진화 : 영상을 흑/백으로 분류하여 처리하는 것
# 임계값보다 크면 백, 작으면 흑으로 분류
# 임계값 함수 / 임계값을 잘 설정하면 두 영상을 구분할 수 있다.
# cv2.threshold(src, thresh, maxval, type, dst=None) -> retval, dst
# src : 입력 영상. 다채널, 8비트 또는 32비트 실수형
# thresh : 사용자 지정 임계값
# maxval : cv2.THRESH_BINARY 또는 cv2.THRESH_BINARY_INV 방법 사용시 최댓값. 보토 255
# type : cv2.THRESH_로 시작하는 플래그. 임계값 함수 동작 지정 또는 자동 임계값 결정 방법 지정
# retval : 사용된 임계값
# dst : 출력 영상. src와 동일 크기, 동일 타입, 같은 채널 수
masks = cv2.threshold(logo, 220, 255, cv2.THRESH_BINARY)[1]  # 로고 영상 이진화

print(len(masks))
masks = cv2.split(masks)
print(len(masks))

fg_pass_mask = cv2.bitwise_or(masks[0], masks[1])
fg_pass_mask = cv2.bitwise_or(masks[2], fg_pass_mask)  # 전경 통과 마스크
bg_pass_mask = cv2.bitwise_not(fg_pass_mask)  # 배경 통과 마스크

(H, W), (h, w) = image.shape[:2], logo.shape[:2]  # 전체 영상, 로고 영상 크기
x, y = (W - w) // 2, (H - h) // 2  # 시작 좌표 계산
roi = image[y : y + h, x : x + w]

# 행렬 논리곱과 마스킹을 이용한 관심 영역 복사
foreground = cv2.bitwise_and(logo, logo, mask=fg_pass_mask)  # 로고의 전경만 복사
background = cv2.bitwise_and(roi, roi, mask=bg_pass_mask)  # 원본 roi의 배경만 복사

dst = cv2.add(background, foreground)  # 로고 전경과 원본 배경 간 합성
image[y : y + h, x : x + w] = dst  # 합성 영상을 원본에 복사

cv2.imshow("background", background)
cv2.imshow("foreground", foreground)
cv2.imshow("dst", dst)
cv2.imshow("image", image)
cv2.waitKey()
  • practice01.py


import cv2
import numpy as np

# circle사진을 BGR로 불러온다.
circle = cv2.imread("images/circle.jpg", cv2.IMREAD_COLOR)


# 특정 색상을 추출할 때는 HSV 색공간을 이용하는게 좋다고 한다. (H, S 색상 조절. V는 밝기 조절)
# 하지만 여기서는 RGB로 해보도록 하자.
if circle is None:
    raise Exception("Error: Unable to read the image.")
else:
    # 색상의 범위를 정한다. (파랑)
    lower_blue = np.array([100, 0, 0])
    upper_blue = np.array([255, 50, 50])

    # 색상의 범위를 정한다. (빨강)
    lower_red = np.array([0, 0, 100])
    upper_red = np.array([50, 50, 255])

    # 색상의 범위를 정한다. (초록)
    lower_green = np.array([0, 100, 0])
    upper_green = np.array([50, 255, 50])

    # inRange함수를 사용하면, 특정 색상 영역을 추출할 수 있다.
    # cv2.inRange(src, lowerb, upperb, dst=None) -> dst
    # 각 컬러의 원들에 대한 마스크를 생성한다.
    # src: 입력 행렬
    # lowerb: 하한 값 행렬 또는 스칼라
    # upperb: 상한 값 행렬 또는 스칼라
    # dst: 입력 영상과 같은 크기의 마스크 영상. (numpy.uint8) 범위 안에 들어가는 픽셀은 255, 나머지는 0으로 설정
    mask_blue = cv2.inRange(circle, lower_blue, upper_blue)
    mask_red = cv2.inRange(circle, lower_red, upper_red)
    mask_green = cv2.inRange(circle, lower_green, upper_green)

    # 컨투어는 등고선을 의미한다. 외곽선을 따는 함수라고 생각하자.
    # cv2.findContours(src, mode, contours, hierarchy, offset)
    # src : 입력 영상, 검장과 흰색으로 구성된 바이너리 이미지
    # mode : 컨투어 제공 방식 (cv2.RETR_EXTERNAL-가장바깥쪽 라인만 생성)
    # method : 근사 값 방식 (cv2.CHAIN_APPROX_NONE-근사 없이 모든 좌표 제공)
    # contours : 검출한 컨투어 좌표(list type)
    # hierarchy : 컨투어 계층 정보(Nextm Prev, FirstChild, Parent, -1 [해당없음])
    # offset : ROI 등으로 인해 이동한 컨투어 좌표의 오프셋
    contours_blue, _ = cv2.findContours(
        mask_blue, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
    )
    contours_red, _ = cv2.findContours(
        mask_red, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
    )
    contours_green, _ = cv2.findContours(
        mask_green, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
    )

    # 원에 사각형 그리기
    # boundingRect : Contour에 외접하는 사각형의 시작점과 width, height를 얻는다.
    # rectangle함수로 사각형을 그린다.
    for contour in contours_blue:
        x, y, w, h = cv2.boundingRect(contour)
        cv2.rectangle(
            circle, (x, y), (x + w, y + h), (255, 0, 0), 2
        )  # Blue rectangle (BGR color format)

    for contour in contours_red:
        x, y, w, h = cv2.boundingRect(contour)
        cv2.rectangle(
            circle, (x, y), (x + w, y + h), (0, 0, 255), 2
        )  # Red rectangle (BGR color format)

    for contour in contours_green:
        x, y, w, h = cv2.boundingRect(contour)
        cv2.rectangle(
            circle, (x, y), (x + w, y + h), (0, 255, 0), 2
        )  # Green rectangle (BGR color format)

    # Display the original image with rectangles
    cv2.imshow("Rectangles around circles", circle)

    # Wait until a key is pressed and then close the window
    cv2.waitKey(0)
    cv2.destroyAllWindows()

히스토그램 스트레칭

  • 명암 대비를 향상시키는 연산으로, 낮은 명암 대비를 보이는 영상의 화질을 향상시키는 방법

import cv2
import numpy as np


def calc_histo(image, histSize, ranges=[0, 256]):  # 행렬 원소의 1차원 히스토그램 계산
    hist = np.zeros((histSize, 1), np.float32)  # 히스토그램 누적 행렬
    gap = ranges[1] / histSize  # 계급 간격

    for i in range(image.shape[0]):  # 2차원 행렬 순회 방식
        for j in range(image.shape[1]):
            idx = int(image.item(i, j) / gap)
            hist[idx] += 1

    return hist


image = cv2.imread("images/dv.jpg", cv2.IMREAD_GRAYSCALE)
if image is None:
    raise Exception("영상 파일 읽기 오류 발생")

hsize, ranges = [32], [0, 256]  # 히스토그램 간격 수(범위), 값 범위
gap = ranges[1] / hsize[0]
ranges_gap = np.arange(0, ranges[1] + 1, gap)
hist1 = calc_histo(image, hsize[0], ranges)
hist2 = cv2.calcHist([image], [0], None, hsize, ranges)  # OpenCV 함수 사용
hist3, bins = np.histogram(image, ranges_gap)

print("User 함수 : \n", hist1.flatten())
print("OpenCV 함수: \n", hist2.flatten())
print("numpy 함수 : \n", hist3)

cv2.imshow("image", image)
cv2.waitKey()


# 이미지를 고르고(어두운 영상이나 명임이 한쪽으로 치우친 영상)
# 스트레칭 기법을 이용해서 영상을 개선시켜보자.



```python

# 이미지를 고르고(어두운 영상이나 명임이 한쪽으로 치우친 영상)
# 스트레칭 기법을 이용해서 영상을 개선시켜보자.

import numpy as np
import cv2


def draw_histo(hist, shape=(200, 256)):
    hist_img = np.full(shape, 255, np.uint8)
    cv2.normalize(hist, hist, 0, shape[0])  # 정규화
    gap = hist_img.shape[1] / hist.shape[0]  # 한 계급 너비

    for i, h in enumerate(hist):
        x = int(round(i * gap))  # 막대 사각형 시작 x좌표
        w = int(round(gap))
        cv2.rectangle(hist_img, (x, 0, w, int(h)), 0, cv2.FILLED)

    return cv2.flip(hist_img, 0)  # 영상 상하 뒤집기 후 반환


def search_value_idx(hist, bias=0):  # 값 있는 첫 계급 검색 함수
    for i in range(hist.shape[0]):
        idx = np.abs(bias - i)
        if hist[idx] > 0:
            return idx  # 위치 반환
    return -1  # 대상이 없으면 반환


image = cv2.imread("images/hist_stretch.jpg", cv2.IMREAD_GRAYSCALE)
if image is None:
    raise Exception("영상파일 읽기 오류")

bsize, ranges = [64], [0, 256]  # 계급 개수 및 화소 범위
hist = cv2.calcHist([image], [0], None, bsize, ranges)

bin_width = ranges[1] / bsize[0]  # 한 계급 너비
low = search_value_idx(hist, 0) * bin_width  # 최저 화소값
high = search_value_idx(hist, bsize[0] - 1) * bin_width  # 최고 화소값

idx = np.arange(0, 256)  # 룩업 인덱스 (0~255) 생성
idx = (idx - low) / (high - low) * 255  # 수치 적용하여 룩업 인덱스 완성
idx[0 : int(low)] = 0  # 히스토그램 하위 부분
idx[int(high + 1) :] = 255  # 히스토그램 상위 부분

dst = cv2.LUT(image, idx.astype("uint8"))  # 룩업 테이블 사용

"""
룩업 테이블을 사용하지 않고 직접 구현
dst = np.zeroes(image.shape, dtype=image.dtype)
for i in range(dst.shape[0]):
    for j in range(dst.shape[1]):
        dst[i,j] = idx[image[i, j]]
"""

hist_dst = cv2.calcHist([dst], [0], None, bsize, ranges)  # 결과 영상 히스토그램 재계산
hist_img = draw_histo(hist, (200, 360))  # 원본 영상 히스토그램 그리기
hist_dst_img = draw_histo(hist_dst, (200, 360))  # 결과 영상 히스토그램 그리기

print("high vlue =", high)
print("low_vlue =", low)
cv2.imshow("image", image)
cv2.imshow("hist_img", hist_img)
cv2.imshow("dst", dst)
cv2.imshow("hist_dst_img", hist_dst_img)
cv2.waitKey(0)

화소 영역 처리

  • 한 화소와 근처 화소를 포함하여 9개의 화소가 서로 관여하여 한 가운데에 새로운 화소를 생성하는 것을 화소 영역 처리라고 함(개수는 때에따라 다름)
  • 화소의 원 값이나 위치를 바탕으로 화소 값을 변경하는 화소의 점 처리와 달리, 해당 입력 화소 뿐만 아니라 그 주위의 화소 값도 함께 고려하는 공간 영역 연산
  • 회선 기법(또는 컨볼루션 기법)으로 수행하므로, 화소의 영역 처리를 회선 처리(Convolution Processing) 또는 컨볼루션 처리라고 한다.
  • 원시 화소와 이웃한 각 화소에 가중치를 곱한 합을 출력 화소로 생성

화소 영역 기반 처리

  • 엠보싱 효과, 블러링, 샤프닝(엣지), 경계선 검출(Edge detection), 잡음 제거 등의 기술이 있다.

회선 마스크


  • 필터링을 이용한 영상처리는 2차원의 컨볼루션을 수행하게 되는데, 이때 사용되는 필터 마스크를 컨보룰션 마스크 또는 회선 마스크라고 한다.

회선 마스크 특징

  • 주변 화소의 값을 각 방향에서 대칭적으로 고려해야 함. 이것은 각 방향에 있는 같은 수의 이웃 화소에 기반을 두고 새로운 화소 값을 생성하기 때문
  • 회선 마스크의 크기는 행과 열 모두 홀수의 크기를 사용하여 3x3, 5x5, 7x7등
  • 회선 처리 기법으로 생성된 출력 영상은 밝기 에너지를 보존해야 하므로 영상의 평균 밝기를 원 영상과 똑같이 유지해야 함.
  • 회선된 영상의 평균 밝기 값이 원본 영상과 같도록 많은 회선 마스크의 계수 합이 1이 되도록 해야함. (항상 그런것은 아니다)

블러링

  • 디지털 카메라로 사진을 찍을 때, 초점이 맞지 않으면 사진이 흐려짐
  • 이러한 현상을 이용해서 영상의 디테일한 부분을 제거하는 아웃 포커싱 기법
    • 포토샵의 뽀샤시 기능
  • *블러링(blurring)
  • 영상에서 화소값이 급격하게 변하는 부분들을 감소시켜 점진적으로 변하게 함으로써 영상이 전체적으로 부드러운 느낌이 나게 하는 기술
  • 화소값이 급격이 변화하는 것을 점진적으로 변하게 하는 방법
    • 블러링 마크스로 회선 수행

샤프닝

  • 적절하게 쓰면 샤프닝, 극단적으로 쓰면 엣지 디텍션
  • 출력 화소에서 이웃 화소끼리 차이를 크게 해서 날카로운 느낌이 나게 만드는 것
  • 영상의 세세한 부분을 강조할 수 있으며, 경계 부분에서 명암대비가 증가되는 효과

샤프닝 마스크

  • 마스크 원소들의 값 차이가 커지도록 구성
  • 마스크 원소 전체합이 1이 되어야 입력영상 밝기가 손실 없이 출력영상 밝기로 유지

그리고 마지막으로 오늘은 호프데이여서 보라매 근처 BHC 치킨집가서 치킨을 마음껏 먹었다 ㅎㅎ