본문 바로가기
카테고리 없음

45Day_2021_08_04 (OpenCV, 이미지 기본 연산, 포화 연산, 밝기 조절, 명암비 조절, 히스토그램 분석, 이미지 합성, 마스크 연산)

by 우리집야옹이룰루 2021. 8. 4.
728x90
반응형
SMALL

이미지 기본 연산

이미지는 numpy배열이므로 정수와 +, -, /, * 

 

import cv2

img = cv2.imread('a.jpg', 0)

cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

a.jpg

 

 

<이미지 밝기 조절>
픽셀 값이 클수록 밝고, 0에 가까울수록 어둡다.

img2 = img + 100  # 이미지 밝게 처리
img3 = img - 100  # 이미지 어둡게 처리
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
cv2.waitKey(0)
cv2.destroyAllWindows()

      img2                                                                                    img3             

 

 


 

<포화 연산>  

색상 값은 0-255 사이의 값을 갖지만 +, - 연산을 수행하면 이 범위를 벗어나는 값을 만들기도 한다. 

그런데 이미지 값의 타입은 uint8이므로 256을 0이 되므로 흰색이 검은색으로 변경된다. 

이렇게 처리되면 아주 밝은 부분이 아주 어둡게, 또는 아주 어두운 부분이 아주 밝게 반대로 표현되어 이미지가 변질된다. 이를 위해서 포화 연산을 처리해주어야 함  
  
포화 연산 : 픽셀 값이 255를 넘으면 255로, 0 미만이면 0으로 처리하는 연산    
  
함수: np.clip(src, 최솟값, 최댓값)  
=> 배열 src 요소중 최솟값보다 작은 것은 모두 최솟값으로, 최댓값보다 큰 값은 모두 최댓값으로 대체해준다.

 

import numpy as np

def saturate_bright(p, num):
    pic = p.copy()
    pic = pic.astype('int32')
    pic = np.clip(pic+num, 0, 255)
    pic = pic.astype('uint8')
    return pic

def saturate_dark(p, num):
    pic = p.copy()
    pic = pic.astype('int32')
    pic = np.clip(pic-num, 0, 255)
    pic = pic.astype('uint8')
    return pic
    
img2 = saturate_bright(img, 100)  #이미지 밝게 처리
img3 = saturate_dark(img, 100)  #이미지 어둡게 처리
cv2.imshow('img', img)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
cv2.waitKey(0)
cv2.destroyAllWindows()

            기본 img                                  saturate_bright처리한 img2                             saturate_dark처리한 img3

 

 


 

이미지 밝기를 트랙 바로 조절

 

import cv2
import numpy as np


def saturate_bright(p, num):
    pic = p.copy()
    pic = pic.astype('int32')
    pic = np.clip(pic + num, 0, 255)
    pic = pic.astype('uint8')
    return pic


def saturate_dark(p, num):
    pic = p.copy()
    pic = pic.astype('int32')
    pic = np.clip(pic - num, 0, 255)
    pic = pic.astype('uint8')
    return pic


def bright(x):
    b = cv2.getTrackbarPos('bright', 'image')
    img2 = saturate_bright(img, b)
    cv2.imshow('image', img2)


def dark(x):
    b = cv2.getTrackbarPos('dark', 'image')
    img2 = saturate_dark(img, b)
    cv2.imshow('image', img2)


img = cv2.imread('a.jpg', cv2.IMREAD_COLOR)
cv2.namedWindow('image')

cv2.createTrackbar('bright', 'image', 0, 100, bright)
cv2.createTrackbar('dark', 'image', 0, 100, dark)

cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

밝기를 높였을 때                                                                              밝기를 낮췄을 때

 


 

<이미지 명암비 조절>

명암비란 이미지의 밝은 부분과 어두운 부분의 밝기 차를 의미.

명암비 조절은 이미지의 밝은 부분은 더 밝게, 이미지의 어두운 부분은 더 어둡게 함으로써 이미지 윤곽을 뚜렷하게 처리하므로 활용도가 높다.

 

- 명암비를 1보다 작게 하면 밝기 차가 줄어들고 전반적으로 어두워진다.

- 명암비를 1보다 크게 하면 밝기 차가 커지고 흰색 영역이 넓어져 전반적으로 밝아진다.

# 이미지 명암비 조절
def saturate_contrast1(p, num):
    pic = p.copy()
    pic = pic.astype('int64')
    pic = np.clip(pic * num, 0, 255)
    pic = pic.astype('uint8')
    return pic
    
# 명암비를 1보다 작게 주면 밝기 차가 줄어들고 전반적으로 어두어짐
img4 = saturate_contrast1(img, 0.5)

# 명암비를 1보다 크게 주면 밝기 차가 커지고 흰색 영역이 넓어짐
img5 = saturate_contrast1(img, 2)

cv2.imshow('img', img)
cv2.imshow('img4', img4)
cv2.imshow('img5', img5)
cv2.waitKey(0)
cv2.destroyAllWindows()

기본 이미지                                               명암비=0.5                                                      명암비=2

  

 

명암비 효율적 조절

픽셀 중간값인 128을 기준으로 이 보다 큰 값은 더 밝게 만들고 128보다 작은 값은 더 어둡게 만듦으로써 대비를 크게 함

dst(x, y) = src(x, y) + (src(x, y)-128)*alpha

 

 

def saturate_contrast2(p, num):
    pic = p.copy()
    pic = pic.astype('int64')
    pic = np.clip(pic + (pic - 128) * num, 0, 255)
    pic = pic.astype('uint8')
    return pic


img4 = saturate_contrast2(img, 0.5)
img5 = saturate_contrast2(img, 2)

cv2.imshow('img', img)
cv2.imshow('img4', img4)
cv2.imshow('img5', img5)
cv2.waitKey(0)
cv2.destroyAllWindows()

기본 이미지                                               명암비=0.5                                                      명암비=2

 

명암비를 효율적으로 조절했을 때 색상이 튀지 않고 좀 더 고르게 나온 것을 볼 수 있다.

 

 


<히스토그램 분석>

영상 픽셀 밝기 분포를 분석하여 밝기, 명암비 조절

cv2.calcHist(img, channel, mask, histSize, range)

  • img: 이미지 배열
  • channel: 분석할 칼라
  • mask: 분석할 영역. None이면 이미지 전체
  • histSize: 히스토그램 크기.
  • x축 값 개수 range: x축 값 범위

 

import cv2
from matplotlib import pyplot as plt

img = cv2.imread('a.jpg', 0)
hist = cv2.calcHist([img], [0], None, [256], [0, 255])
plt.subplot(2, 1, 1), plt.imshow(img, 'gray')
plt.subplot(2, 1, 2), plt.plot(hist, color='r')
plt.xlim([0, 256])
plt.show()

 

 

 

 

이미지를 밝게 조절한 뒤 히스토그램 하면 픽셀 분포가 오른쪽으로 이동

img2 = saturate_bright(img, 30)
hist = cv2.calcHist([img2], [0], None, [256], [0, 256])
plt.subplot(2, 1, 1), plt.imshow(img2, 'gray')
plt.subplot(2, 1, 2), plt.plot(hist, color='r')
plt.xlim([0, 256])
plt.show()

 

 

 

 

이미지를 어둡게 처리한 뒤 히스토그램 하면 픽셀 분포가 왼쪽으로 이동

img3 = saturate_dark(img, 30)
hist = cv2.calcHist([img3], [0], None, [256], [0, 256])
plt.subplot(2, 1, 1), plt.imshow(img3, 'gray')
plt.subplot(2, 1, 2), plt.plot(hist, color='r')
plt.xlim([0, 256])
plt.show()

 

 

 

명암비를 크게 주고 히스토그램 하면 분포가 아주 밝고, 어두운 쪽으로 이동

img4 = saturate_contrast2(img, 2)
hist = cv2.calcHist([img4], [0], None, [256], [0, 256])
plt.subplot(2, 1, 1), plt.imshow(img4, 'gray')
plt.subplot(2, 1, 2), plt.plot(hist, color='r')
plt.xlim([0, 256])
plt.show()

 

 

 

명암비를 작게 주고 히스토그램 하면 분포 영역이 더 작아진다.

이는 이미지가 비슷한 색상에 몰려있고 이는 선명하지 않음을 의미한다.

img5 = saturate_contrast2(img, -0.5)
hist = cv2.calcHist([img5], [0], None, [256], [0, 256])
plt.subplot(2, 1, 1), plt.imshow(img5, 'gray')
plt.subplot(2, 1, 2), plt.plot(hist, color='r')
plt.xlim([0, 256])
plt.show()

 

 

 

 

위 그림처럼 흐릿한 이미지를 선명하게 하는 방법으로 히스토그램 스트레칭이 사용될 수 있다.

히스토그램 스트레칭은 분포를 양옆으로 펼쳐준다.

dst(x, y) = (src(x, y) - 최소 픽셀 값)*255/(최대 픽셀 값 - 최소 픽셀 값)

최소 픽셀 값 : 가장 어두운 부분, 최대 픽셀 값 : 가장 밝은 부분

f_max = img5.max()
f_min = img5.min()

nframe = img5.astype('int64')
img6 = np.clip((nframe - f_min) * 255 / (f_max - f_min), 0, 255).astype('uint8')
hist = cv2.calcHist([img6], [0], None, [256], [0, 256])
plt.subplot(2, 1, 1), plt.imshow(img6, 'gray')
plt.subplot(2, 1, 2), plt.plot(hist, color='r')
plt.xlim([0, 256])
plt.show()

 

 

 

 

 

히스토그램 스트레칭은 분포도를 넓혀주지만 픽셀 값의 평준화는 해주지 않는다.

히스토그램 평활화는 그래프에서 너무 돌출되는 부분을 깎아주어 평준화해준다

dst(x, y) = round((누적합(src(x, y))*픽셀 최댓값)/(픽셀 누적 최댓값-최솟값))

 

hist, bins = np.histogram(img5.flatten(), 256, [0, 256])

cdf = hist.cumsum()  # 누적합. 각 빈의 누적합 계산

# 빼도 상관없음
cdf_m = np.ma.masked_equal(cdf, 0)  # 속도개선을 위해 0인 부분 제외

# 히스토그램 평활화
cdf_m = (cdf_m - cdf_m.min()) * 255 / (cdf_m.max() - cdf_m.min())


# Mask처리를 했던 부분을 다시 0으로 변환
# 빼도 상관없음
cdf = np.ma.filled(cdf_m, 0).astype('uint8')

img7 = cdf[img5]  # img5의 값이 cdf배열의 인덱스로 사용됨
                  # cdf는 히스토그램 평활화된 값이 저장되어 있으므로
                  # img5[12][10]칸의 픽셀값이 125이면 cdf[125]의 값을 추출
                  # 이 값은 픽셀값 125가 평활화된 값이다

hist = cv2.calcHist([img7], [0], None, [256], [0, 256])
plt.subplot(3, 1, 1), plt.imshow(img5, 'gray')
plt.subplot(3, 1, 2), plt.imshow(img7, 'gray')
plt.subplot(3, 1, 3), plt.plot(hist, color='r')
plt.xlim([0, 256])
plt.show()

 

 

 

 

 

 

 

히스토그램 평활화 함수는 cv2.equalizeHist() 이므로 위 코드를 이 함수를 모두 표현할 수 있다.

 

img8 = cv2.equalizeHist(img5)
hist = cv2.calcHist([img8], [0], None, [256], [0, 256])
plt.subplot(3, 1, 1), plt.imshow(img5, 'gray')
plt.subplot(3, 1, 2), plt.imshow(img8, 'gray')
plt.subplot(3, 1, 3), plt.plot(hist, color='r')
plt.xlim([0, 256])
plt.show()

 

 


 

 

이미지에 and, or, not, xor 연산 수행

이미지 연산은 각 픽셀의 b, g, r bit별 수행

 

 

import cv2

img1 = cv2.imread('1.jpg')
img2 = cv2.imread('2.jpg')

img3 = cv2.bitwise_and(img1, img2)
img4 = cv2.bitwise_or(img1, img2)
img5 = cv2.bitwise_not(img2)
img6 = cv2.bitwise_xor(img1, img2)

imgh1 = cv2.hconcat([img1, img2, img3])
imgh2 = cv2.hconcat([img4, img5, img6])

res = cv2.vconcat([imgh1, imgh2])

cv2.imshow('img', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

출력 결과

 

 

 


 

<이미지 합성>

두 이미지의 픽셀 값을 더하여 구한다.

 

img1 = cv2.imread('a.jpg', 1)
                                        # 보간방법 선택. INTER_AREA는 축소. 확대에는 LInear, cubic
img1 = cv2.resize(img1, dsize=(640,410), interpolation=cv2.INTER_AREA)
img2 = cv2.imread('b.jpg', 1)
img3 = img1 + img2

cv2.imshow('img3', img3)

cv2.waitKey(0)
cv2.destroyAllWindows()

 

 

 

하지만 그냥 더하면 포화 문제가 발생하므로 포화 연산 추가해준다.

 

# 연산할 두개의 이미지를 받아온다.
def saturate_add(p1, p2):
    pic1 = p1.copy()
    pic2 = p2.copy()

    pic1 = pic1.astype('int64')
    pic2 = pic2.astype('int64')

    pic = np.clip(pic1 + pic2, 0, 255)
    pic = pic.astype('uint8')
    return pic


img3 = saturate_add(img1, img2)
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
cv2.waitKey(0)
cv2.destroyAllWindows()

img1                                                                     img2                                                          img3= img1+img2    

 

 

<이미지 합성 함수>

cv2.add(img1, img2) 함수를 이용하면 자동으로 포화 연산이 되어서 나온다.

 

 

img5 = cv2.add(img1, img2)
cv2.imshow('img3', img3)
cv2.imshow('img5', img5)
cv2.waitKey(0)
cv2.destroyAllWindows()

img3 : opencv 함수를 사용하지 않고 합성.                                              img5 :  opencv 함수인 cv2.add(img1, img2) 사용

 

 


<가중치 합성 함수>

이미지 합성 시 원본 이미지에 가중치를 주어 덧셈.

가중치의 합은 1이어야 하고 가중치가 큰 쪽 이미지가 강하게 나옴

dst(x, y) = alpha * src1(x, y) + beta * src2(x, y)

def saturate_addWeight(p1, a1, p2, a2):
    pic1 = p1.copy()
    pic2 = p2.copy()

    pic1 = pic1.astype('int64')
    pic2 = pic2.astype('int64')

    pic = np.clip(a1 * pic1 + a2 * pic2, 0, 255)

    pic = pic.astype('uint8')
    return pic


img3 = saturate_addWeight(img1, 0.4, img2, 0.6)

cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
cv2.waitKey(0)
cv2.destroyAllWindows()

img1                                                            img2                                                        img1*0.4 + img2*0.6=>img3

 

 

 

 

 

<가중치 합성 함수>

cv2.addWeighted(img1, 비율, img2, 비율, 결과물에 더할 값)

img5 = cv2.addWeighted(img1, 0.4, img2, 0.6, 0)
cv2.imshow('img3', img3)
cv2.imshow('img5', img5)
cv2.waitKey(0)
cv2.destroyAllWindows()

img3 : opencv 함수를 사용하지 않고 합성.                    img5 :  opencv 함수인 cv2.addWeighted(img1, 비율, img2, 비율, 결과물에 더할 값) 사용

 

 


 

트랙 바를 사용하여 두 이미지 합성 비율을 조절하는 프로그램

 

 

import cv2

def add(pos):
    pic = cv2.addWeighted(img1, (100-pos)/100, img2, pos/100, 0)
    cv2.imshow('img', pic)
    
img1 = cv2.imread('face1.jpg', 1)
img2 = cv2.imread('face2.jpg', 1)
img1 = cv2.resize(img1, (320, 270))
img2 = cv2.resize(img2, (320, 270))

cv2.imshow('img', img1)
cv2.createTrackbar('add', 'img', 0, 100, add)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

 

 

 


<마스크 연산>

추출하고 싶은 객체를 제외한 나머지 배경을 0으로 처리하면 배경은 검은색, 객체는 그대로인 이미지가 된다.

이를 합성하려는 이미지와 더하면 배경은 0이므로 합성하는 이미지의 내용 그대로, 객체는 위로 붙게 된다.

 

 

import cv2
import numpy as np

background = cv2.imread('b.jpg', 1)
cv2.imshow('background', background)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

이 사진의 (150, 150)에 daum 로고를 합성해서 넣는다.

 

daum_logo = cv2.imread('daum.png', 1)

cv2.imshow('daum_logo', daum_logo)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

h, w, _ = daum_logo.shape  # 로고의 세로 가로 길이 읽음

roi = background[150:150 + h, 150:150 + w]  # 로고 크기 만큼 (150, 150)위치에서 roi 추출

img = cv2.add(roi, daum_logo)

black = np.zeros((h, w, 3), dtype='uint8')
broi = black + roi

# 로고의 배경을 검정으로 변경
# 값이 255인 픽셀의 값을 모두 0으로 변경
daum_logo[daum_logo[:] == 255] = 0
# 이미지 흑백 처리
mask = cv2.cvtColor(daum_logo, cv2.COLOR_BGR2GRAY)
# 글자는 회색, 배경은 검은색

# 글자를 흰색변경 => 이 마스크를 로고와 and하면 배경이 잘려나감
mask[mask[:] > 0] = 255

# 배경은 흰색, 글자는 검정 => roi와 and하면 roi의 배경은 그대로, 로고 글자모양만 검정으로 파낸 효과
mask_inv = cv2.bitwise_not(mask)

# 흑백을 컬러로 변환 (변경하지 않으면 차수가 맞지 않아서 에러가 난다.)
mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
mask_inv = cv2.cvtColor(mask_inv, cv2.COLOR_GRAY2BGR)

back = cv2.bitwise_and(roi, mask_inv)
logo = cv2.bitwise_and(daum_logo, mask)
res = cv2.add(back, logo)

background[150:150 + h, 150:150 + w] = res
cv2.imshow('background', background)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

728x90
반응형
LIST