퐈니썬's LIfe - 잘 실패하자 RSS 태그 관리 글쓰기 방명록
영상처리 (6)
2022-02-15 00:10:52
728x90
반응형

<Introduction>

본 포스트에서느 이미지 처리 중 "밝기조절" 과 "대조도"에 대해 알아보고 구현해보고자 합니다.


<Luminosity>

명도라고도 불리는 밝기는 이미지의 픽셀 값에 사용자가 설정하여 입력할 밝기도를 정하여 픽셀값에 더해주면 됩니다. 

 

픽셀의 값이 증가하면, 이미지가 밝아집니다. 반면에 픽셀의 값이 감소하면 어둬어집니다.

 

$$ LuminosityImageValue = ImageValue + LuminosityValue $$

 

간단하게 이미지의 픽셀을 불러와서 명도 수치를 더해주면 됩니다!

 

<Luminosity - 구현>

from PIL import Image, ImageDraw

# 설정하고자 하는 밝기도
luminosity = 80

# 이미지 로드
input_image = Image.open("input.png")
input_pixels = input_image.load()

# 출력 이미지 생성
output_image = Image.new("RGB", input_image.size)
draw = ImageDraw.Draw(output_image)

# 이미지의 픽셀에 밝기도 더하여 출력
for x in range(output_image.width):
    for y in range(output_image.height):
        r, g, b = input_pixels[x, y]
        r = int(r + luminosity)
        g = int(g + luminosity)
        b = int(b + luminosity)
        draw.point((x, y), (r, g, b))

output_image.save("output.png")


<Contrast 대조도>

Contrast는 이미지의 밝은 부분과 어두운 부분의 차이를 의미하고, 이를 조절한다는 것은 이미지의 밝은 부분과 어두운부분의 차이를 조절한다는 의미입니다. 이는 이미지내의 물체를 선명하게 보는 효과를 가지고 옵니다. 

 

예를들어, 이미지 안의 픽셀들이 대부분은 비슷한 Intensity(값)을 가진다면 어떨까요?

 

이미지는 아래와 같이 어떠한 물체 정보도 얻기 힘들 것입니다. 

 

 

이 경우를 대조도가 낮다 라고 말할 수 있습니다. 반면에, 대조도가 높다는 것은 이미지 내의 물체를 식별하는데 용이하고 이는 선명하다 라는 느낌을 주게됩니다. 

 

<Contrast 대조도 - 구현>

앞서 말씀드린 것처럼 대조도가 높다는 것은 이미지 내의 물체를 식별하는데 용이하고, 선명하다 라는 느낌을 주게됩니다. 즉, 대개는 대조도를 높여 이미지를 선명하게 하는 것이 이미지의 질을 높이는 방향일 것입니다. 

 

어떻게 대조도를 만드느냐에 대해는 여러가지 방법론이 있습니다. 하지만 핵심은 "intensity 값의 곱" 입니다.

 

즉, 입력이미지의 픽셀에 어떠한 조정 값을 곱하여 대조도를 변경 할 수 있습니다. 

 

예를 들어,  A라는 픽셀은 100이라는 Intensity를 가지고, B라는 픽셀은 50이라는  intenstiy를 가진다고 가정합니다.

 

Contrast를 높이기 위해 두 픽셀 값에 대해 1.5를 곱한다고 하면, A= 150, B=75가 됩니다.

 

1.5를 곱하기 전의 A-B의 값은 50이였지만, 1.5를 곱한 후의 A-B의 값은 75입니다.

 

즉, 두 픽셀의 값의 차이가 더 벌어진 것입니다. 이러한 원리로 대조도를 조정합니다.

 

이때 곱해지는 값을 정하는 방법은 여러가지가 있습니다.

 

from PIL import Image, ImageDraw

# Load image:
input_image = Image.open("input.png")
input_pixels = input_image.load()

# Create output image
output_image = Image.new("RGB", input_image.size)
draw = ImageDraw.Draw(output_image)

# Find minimum and maximum luminosity
imin = 255
imax = 0
for x in range(input_image.width):
    for y in range(input_image.height):
        r, g, b = input_pixels[x, y]
        i = (r + g + b) / 3
        imin = min(imin, i)
        imax = max(imax, i)

# Generate image
for x in range(output_image.width):
    for y in range(output_image.height):
        r, g, b = input_pixels[x, y]
        # Current luminosity
        i = (r + g + b) / 3
        # New luminosity
        ip = 255 * (i - imin) / (imax - imin)
        r = int(r * ip / i)
        g = int(g * ip / i)
        b = int(b * ip / i)
        draw.point((x, y), (r, g, b))

output_image.save("output.png")

 

 

728x90
반응형
2022-01-21 00:19:05
728x90
반응형

<Introduction>

입력 이미지를 "상하좌우 대칭 변환 (Image Flip)"을 구현해 보고자 합니다.

opencv 라이브러리를 통해 간단하게 진행이 가능하지만, 전반적인 이미지 처리에 대한 이해를 다지고자 작성하였습니다. 

 

<Flip>

이미지 대칭 변환(Flip)은 2차원에서는 width 방향, height 방향에 대하여 반전이 가능합니다.

입력 이미지의 크기와 이미지 대칭 변환을 한 출력 이미지의 크기는 동일합니다. 

 

입력 이미지의 크기가 (w, h)일때, 입력 이미지 위의 좌표 (x, y)는 다음과 같은 수식의 형태로 결과 이미지에서 좌표 (x', y')에 매핑합니다. 

 

[상하 대칭 변환]

$$  x' = x $$

$$  y' = h -1 -y $$

 

[좌우 대칭 변환]

$$  x' = w -1 -x $$

$$  y' = y $$

 

<구현>

import cv2
import numpy as np
#Load image
img = cv2.imread('./../Image02.png')
height, width = img.shape[0], img.shape[1]
#Flip
output = np.zeros((height, width,3), dtype=np.uint8)

for x in range(height):
    for y in range(width):
        xp = height - x - 1       #상하 반전
        yp = width - y - 1        #좌우 반전
        output[x,y] = img[xp, y] #상하 반전
        # output[x, y] = img[x, yp] #좌우 반전

cv2.imwrite('./flip_implement.png', output)

 

 

728x90
반응형
2022-01-20 00:05:31
728x90
반응형

<Introduction>

입력 이미지를 특정 사이즈에 맞게 Scale 하는 과정을 python으로 직접 구현하고자 합니다. 

opencv 라이브러리를 통해 간단하게 진행이 가능하지만, 전반적인 이미지 처리에 대한 이해를 다지고자 작성하였습니다. 

 

<Scale(Scaling)>

이미지 스케일링(Scaling)은 입력 이미지를 특정 사이즈에 맞게 줄였다가, 늘렸다가 하여 이미지를 구성하는 이미지 처리방법입니다. 즉, 이미지를 확대, 축소하는 과정입니다. 

 

그렇다면, 입력 이미지 대비 출력 이미지(특정 사이즈)의 비율이 중요하게 되고 그 비율만큼 입력 이미지는 확대 및 축소를 하게 될 것입니다. 

 

잠깐 상상을 해봅시다. 입력 이미지가 100x100의 사이즈를 가지고 해당 이미지를 200x200으로 만들고자 합니다. 즉 확대한 이미지를 만들고자 합니다.

 

이때, 픽셀의 갯수는 4배가 증가할 것입니다. 그러면 1개인 픽셀이 4개가 되는데 그 나머지 3개의 픽셀은 어떻게 할 것인가?

 

opencv에서는 다양한 보간법 제공하여 처리합니다. 구현에서는 인접한 픽셀을 그대로 취하도록 하였습니다. 

 

앞서 예시에서 3개의 픽셀은 1개의 픽셀과 동일한 값을 가지고 옵니다. 

출처 https://www.geeksforgeeks.org/image-processing-without-opencv-python/

 

 

<구현>

import cv2
import numpy as np
from math import floor

#Load image
img = cv2.imread('./../Image01.png')
print(img.shape, img.dtype)
height = img.shape[0]
width = img.shape[1]

#Scale
target_size = (300, 300)  # target size
output = np.zeros((target_size[0], target_size[1], 3), np.uint8)

x_scale = height/output.shape[0]   #input image / output image
y_scale = width/output.shape[1]   #input image / output image

for y in range(output.shape[1]):
    for x in range(output.shape[0]):
        # the pixel at coordinate (x, y) in the new image is equal to the pixel that is located at coordinate (floor(x * x_ratio), floor(y * y_ratio)).
        # floor는 인접한 픽셀을 가져오기 위해서 사용
        xp, yp = floor(x* x_scale), floor(y * y_scale)
        print(xp, yp)
        print(x, y)
        output[x,y] = img[xp,yp]
cv2.imwrite('./scale.png', output)

 

 

<opencv>

# cv2.reize()로 이미지 확대 및 축소 (scale_resize.py)

import cv2
import numpy as np

img = cv2.imread('../Image01.png')
height, width = img.shape[:2]

target_size = (300,300)
scale_img = cv2.resize(img, (target_size[0], target_size[1]), \
                         interpolation=cv2.INTER_AREA)

cv2.imwrite('./scale_cv.png', scale_img)

resize 함수를 통하여 간단하게 구현이 가능하며, 보간법은 영상 축소 시 효과적인 INTER_AREA(Nearest Neigbour 계열)을 사용하였습니다. 

 

 

 

728x90
반응형
2022-01-19 00:24:27
728x90
반응형

<Introduction>

이미지를 표현함에 있어서 컬러를 입히는 것은 굉장히 큰 차이를 보여줍니다. 동일한 이미지라도 어떤 색채냐에 따라 느낌과 표현이 다릅니다. 

 

즉, 획득하는 정보가 다르다는 의미입니다.

 

이미지를 표현하는 색상 방법에는 트루컬러, 의사 컬러, 수도 컬러, 그레이스케일이 있습니다. 또한, 수도 컬러(pseudo color)는 의사 컬러(false color)에 포함되지만, 전형적으로 많이 사용되어서 따로 분류를 하는 것 같습니다. 

이 네 가지 색상 표현 방법에 대해 알아보고자 합니다.

 

<True color>

트루 컬러 (True color)는 말 그대로 우리가 보는 세상과 가장 비슷하게 표현된 색상 표현 방법입니다. RGB로 표현되고 필요에 따라 투명도에 대한 정보까지 표현합니다.
한 픽셀이 가지는 bit수는 24bit(R,G,B each 8bit) + 8bit(투명도)를 가지니 보통은 한 픽셀은 4byte 용량을 가집니다.
다시 컬러로 돌아가면 RGB 24bit이니깐 결국 한 pixel이 표현가능한 색상은 2^24 만큼이 됩니다. 

 

 

 

<False color>

의사 컬러(False color)는 어떤 기준에 따라서 컬러 맵핑을 하는 방법입니다. 즉, 실제 색상이 아니라  특별하게 필요한 정보를 얻기 위해 시각화하기 위해 색채를 입히는 방법이라 할 수 있습니다. 

 

 

 

<Pseudo color>

수도 컬러(Pseudo color)는 의사 컬러 안에 포함되어 있는 색상 표현 방법입니다. 실제 색상이 아니라 시각화에 필요한 컬러로 보통 rainbow or jet을 사용하는 색상 표현 방법입니다. 의료 영상쪽에서는 PET이나 RTDose 값을 수도 컬러로 많이 표현합니다. 이미지의 픽셀 값의 높낮이가 중요한 경우 대조가 확실해서 보기 수월하기 때문입니다. 

 

 

 

 

<Gray scale>

그레이 스케일(Gray scale)은 흑백 영상으로 표현하는 방법으로 RGB가 아닌 단일 값을 가집니다. 즉 pixel이 가지는 데이터 양은 8bit이며 가질 수 있는 값은 2^8로써 0~255까지 표현이 됩니다.

 

728x90
반응형
2022-01-18 15:12:15
728x90
반응형

<Introduction>

입력 이미지를 특정 위치의 bounding box로 cropping (잘라내기) 하는 과정을 python으로 직접 구현하고자합니다. 

opencv 라이브러리를 통해 간단하게 진행이 가능하지만, 전반적인 이미지 처리에 대한 이해를 다지고자 작성하였습니다. 

 

 

<Cropping>

Cropping은 입력 이미지의 특정 부분을 잘라내어 하나의 이미지로 생성하는 과정입니다. 

특정 부분은 Bounding box 형태로 4 개의 좌표가 주어져야 합니다. 

 

start X, Y와 end X, Y 가 주어졌다고 가정했을때, Cropping 되는 이미지의 사이즈는 Bounding box의 사이즈와 동일하게 출력되어야 합니다.

 

출력 이미지 size = (end[X] - start[X], end[Y] - start[Y])

 

우리가 출력하고자하는 start X, Y와 end X, Y를 꼭지점으로 하는 bounding box는 입력 이미지의 좌표가 됩니다. 

 

즉, 입력 이미지의 start X ~ end X 좌표와 start Y ~ end Y 좌표에 픽셀 값을 출력 이미지의 x, y 좌표에 하나씩 입력해주는 과정을 거치면 됩니다. 

 

<입력 이미지>

 

<구현>

import  cv2
import  numpy as np
#Load image
img = cv2.imread('Image01.png')
print(img.shape, img.dtype)

#Cropped data
start = (200, 100)
end = (320, 230)
output = np.zeros((end[0]-start[0], end[1]-start[1], 3), np.uint8)
print(output.shape)
for y in range(output.shape[1]):
    for x in range(output.shape[0]):
        xp, yp = x + start[0], y+start[1]
        output[x,y] = img[xp,yp]
# save result
print(output.shape)
cv2.imwrite('./cropped image02.png',output)

 

 

<opencv>

import cv2
#Load image
img = cv2.imread('./Image01.png')
cv2.imshow('original', img)

#Cropped data
start = (200, 100)
end = (320, 230)
output = img[start[0]:end[0], start[1]:end[1]]
cv2.imwrite('./cropped image03.png',output)

 

728x90
반응형
2022-01-17 21:58:35
728x90
반응형

<Introduction>

입력 이미지를 그레이 스케일 이미지로 변환하는 내용을 python으로 직접 구현하고자 합니다. 

opencv 라이브러리를 통해 간단하게 진행이 가능하지만, 전반적인 이미지 처리에 대한 이해를 다지고자 작성하였습니다. 

 

 

 

<RGB -> Grayscale>

입력 이미지는 RGB로 구성된 이미지를 적용할 것입니다.

RGB로 구성된 이미지를 Grayscale로 바꾸기 위해서는 색상 채널을 변경해야 합니다.

즉, N x N x 3 으로 구성된 이미지를 N x N x 1로 표현해야 할 것입니다.

 

그러기 위해서는 한 픽셀에 대해 가지고 있는 RGB 색상을 평균하여 하나의 값으로 만들어 주어 다시 값을 입력하면 됩니다. 

 

 

<구현>

import numpy as np
import cv2

#load image
img = cv2.imread('./Image01.png')
print(f"image shape: {img.shape} \n data type: {img.dtype}\n")
#view image
cv2.imshow('original image',img)
cv2.waitKey(0)
#get original image attribute
height = img.shape[0]
width = img.shape[1]
#create target array for gray scale
gray_image = np.zeros((height, width, 1), dtype=np.uint8)

print("converting...\n")

# 입력 이미지의 pixel 값 하나씩 불러서 변경
for h in range(height):
    for w in range(width):
        b = img[h, w, 0].astype(np.float32) # RGB의 Blue 값 입력받기
        g = img[h, w, 1].astype(np.float32) # RGB의 Green 값 입력받기
        r = img[h, w, 2].astype(np.float32) # RGB의 Red 값 입력받기

        intensity = (b+g+r)/3               # RGB 채널의 값을 하나의 값으로 평균 내기
        gray_image[h, w, 0] = intensity     # target array에 값 저장
        
print("done")
cv2.imshow("converted grayimage", gray_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

<결과>

<입력 이미지>

 

 

<출력 이미지>

 

728x90
반응형