안녕하세요. 코딩산책입니다.
이미지 처리에서 산술 및 논리 연산은 두 개 이상의 이미지를 결합하거나, 이미지의 특정 부분을 수정하는 데 매우 유용하게 사용됩니다. 이 글에서는 OpenCV와 Python을 사용하여 이미지 간의 산술 및 논리 연산을 적용하는 방법을 다루어 보겠습니다. 이를 통해 이미지 합성, 밝기 및 대비 조정, 이미지 마스킹 등의 작업을 수행할 수 있습니다.
1. 산술 연산 (Arithmetic Operations)
OpenCV는 두 이미지 간의 덧셈, 뺄셈, 곱셈, 나눗셈 등의 산술 연산을 쉽게 수행할 수 있도록 다양한 함수를 제공합니다. 산술 연산을 통해 두 이미지를 결합하거나, 이미지의 밝기를 조정하는 등의 작업을 할 수 있습니다. 두 이미지 간의 곱셈이나 나눗셈은 이미지의 대비를 조정하는 작업에 사용은 가능하지만 거의 사용하지 않으며, 덧셈과 뺄셈을 주로 사용합니다. saturate는 8bit 이미지의 밝기 값은 0~255만을 표현하기 때문에, 범위를 벗어나는 경우에는 0 혹은 255로 값을 대체한다.
• cv2.add(): 이미지의 픽셀 값들에 상수를 더하거나 두 이미지의 픽셀 값을 더한다.
\[result(x,y)=saturate(img1(x,y)+constant)\]
\[result(x,y)=saturate(img1(x,y)+img2(x,y))\]
• cv2.subtract(): 이미지의 픽셀 값들에 상수를 빼거나 두 이미지의 픽셀 값을 뺀다.
\[result(x,y)=saturate(img1(x,y)-constant)\]
\[result(x,y)=saturate(img1(x,y)-img2(x,y))\]
• cv2.multiply(): 이미지의 픽셀 값들에 상수를 곱하거나 두 이미지의 픽셀 값을 곱한다.
\[result(x,y)=saturate(img1(x,y)*constant)\]
\[result(x,y)=saturate(img1(x,y)*img2(x,y))\]
• cv2.divide(): 이미지의 픽셀 값들에 상수를 나누거나 두 이미지의 픽셀 값을 나눈다.
\[result(x,y)=saturate(img1(x,y)/constant)\]
\[result(x,y)=saturate(img1(x,y)/img2(x,y))\]
1.1. 이미지와 상수 간 덧셈과 곱셈 연산
이미지와 상수간 산술연산 중에서도 덧셈과 곱셈 연산을 사용하면 이미지의 밝기와 대비를 상수를 이용하여 쉽게 조정할 수 있습니다.
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('resources/lena.bmp', cv2.IMREAD_GRAYSCALE)
# Increase brightness (add constant)
bright_img = np.clip(cv2.add(img, 100), 0, 255).astype(np.uint8)
# Increase contrast (multiply by a constant)
contrast_img = np.clip(cv2.multiply(img, 1.5), 0, 255).astype(np.uint8)
plt.figure(figsize=(15, 4), linewidth=2)
plt.subplot(1, 3, 1)
plt.imshow(img, cmap='gray')
plt.title('Original Image')
plt.axis('off')
plt.subplot(1, 3, 2)
plt.imshow(bright_img, cmap='gray')
plt.title('Bright Image')
plt.axis('off')
plt.subplot(1, 3, 3)
plt.imshow(contrast_img, cmap='gray')
plt.title('Contrast Image')
plt.axis('off')
# Show plot
plt.tight_layout()
plt.savefig('results/arithmetic.png', dpi=200, facecolor='#eeeeee', edgecolor='black')
plt.show()
1.2. 이미지 간 덧셈 연산
import cv2
import numpy as np
# Load image
img1 = cv2.imread('resources/lena.bmp', cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread('resources/baboon.bmp', cv2.IMREAD_GRAYSCALE)
# Resize images (scale to the same size)
img1 = cv2.resize(img1, (512, 512))
img2 = cv2.resize(img2, (512, 512))
# Image addition operation
added_image = np.clip(cv2.add(img1, img2), 0, 255).astype(np.uint8)
cv2.imshow('Added Image', added_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
1.3. 이미지 간 뺄셈 연산
이미지 간 뺄셈 연산은 이미지 마스킹으로 사용할 수 있습니다. 영상처리에서 많이 사용하는 예제로는 그라데이션 마스크를 사용해 특정 영역을 마스킹 하는 방식을 볼 수 있습니다. 그라데이션 마스크는 픽셀 값이 0에서 255로 점진적으로 변화하는 마스크로, 이를 통해 이미지의 일부 영역을 부드럽게 처리할 수 있습니다. 그라데이션 마스크를 만들기 위해 Sigmoid 함수를 사용해 그라데이션을 생성한 후, 이를 원 모양으로 확산하여 그라데이션 마스크 이미지를 만들 수 있습니다. 만들어진 그라데이션 마스크를 사용해 뺄셈 연산을 수행하면 아래의 결과와 같이 확인 할 수 있습니다.
import cv2
import numpy as np
# Load image (convert to black and white image)
img = cv2.imread('resources/lena.bmp', cv2.IMREAD_GRAYSCALE)
# Check the image size and create a gradient mask of the same size
height, width = img.shape[:2]
# Set the center coordinates and radius of the circle
center = (width // 2, height // 2)
radius = 200
# Create coordinates for creating gradients
X, Y = np.meshgrid(np.arange(width), np.arange(height))
# # Calculating distance from the center of the image
dist_from_center = np.sqrt((X - center[0])**2 + (Y-center[1])**2)
# Calculating gradient using sigmoid function
scale = 0.05
gradient_mask = 1 / (1 + np.exp(-scale * (dist_from_center - radius)))
gradient_mask = gradient_mask * 255
gradient_mask = gradient_mask.astype(np.uint8)
# Subtraction operation
subtracted_image1 = cv2.subtract(img, gradient_mask)
subtracted_image2 = cv2.subtract(gradient_mask, img)
cv2.imshow('Original Image', img)
cv2.imshow('Gradient Mask', gradient_mask)
cv2.imshow('Subtracted Image1', subtracted_image1)
cv2.imshow('Subtracted Image2', subtracted_image2)
cv2.waitKey(0)
cv2.destroyAllWindows()
1.4. cv2.addWeighted 산술 연산
OpenCV의 addWeighted() 함수는 두 이미지를 가중치를 적용하여 병합할 수 있는 매우 유용한 함수입니다. 이를 사용하면 두 이미지 간의 선형 보간(Linear Interpolation)을 통해 자연스러운 합성을 할 수 있습니다.
\[dst(x,y)=saturate(\alpha \cdot src1(x,y)+\beta \cdot src2(x,y)+\gamma)\]
OpenCV에서 제공되는 함수는 cv2.addWeighted(src1, alpha, src2, beta, gamma) 이다.
• src1, src2: 합성할 두 이미지
• alpha: 첫 번째 이미지의 가중치
• beta: 두 번째 이미지의 가중치
• gamma: 결과 이미지에 더할 추가값 (일반적으로 0)
import cv2
# Load image
img1 = cv2.imread('resources/lena.bmp', cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread('resources/airplane.bmp', cv2.IMREAD_GRAYSCALE)
# Resize images (scale to the same size)
img1 = cv2.resize(img1, (512, 512))
img2 = cv2.resize(img2, (512, 512))
# Merge image weights
blended_image = cv2.addWeighted(img1, 0.7, img2, 0.3, 0)
cv2.imshow('Blended Image', blended_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
2 논리 연산 (Logical Operations)
OpenCV에서 제공하는 논리 연산(bitwise operations)은 이미지 처리에서 특정 영역을 마스킹하거나 합성할 때 매우 유용하게 사용됩니다. 논리 연산은 각 픽셀 값에 대해 비트 단위로 연산을 수행하며, 일반적으로 이미지 간의 연산이나 마스크를 사용하여 특정 영역만 처리할 때 사용됩니다.
• cv2.bitwise_and(): AND 연산\((A \land B)\)은 두 이미지의 픽셀 값이 모두 1일 때만 결과가 1이 됩니다. 그렇지 않으면 결과는 0입니다.
\[\text{Result}(x, y) = A(x, y) \land B(x, y)\]
A (픽셀 값) | B (픽셀 값) | A AND B |
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
• cv2.bitwise_or(): OR 연산\((A \lor B)\)은 두 이미지 중 하나라도 1이면 결과가 1이 됩니다.
\[\text{Result}(x, y) = A(x, y) \lor B(x, y)\]
A (픽셀 값) | B (픽셀 값) | A OR B |
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
• cv2.bitwise_xor(): XOR 연산\((A \oplus B)\)은 두 값이 다를 때 1이 됩니다. 즉, A와 B가 서로 다른 경우에만 결과가 1입니다.
\[\text{Result}(x, y) = A(x, y) \oplus B(x, y)\]
A (픽셀 값) | B (픽셀 값) | A XOR B |
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
• cv2.bitwise_not(): NOT 연산\((\neg A)\)은 하나의 이미지 또는 픽셀에 적용되며, 각 비트를 반전시킵니다. 즉, 1은 0으로, 0은 1로 변환됩니다.
\[\text{Result}(x, y) = \neg A(x, y)\]
A (픽셀 값) | NOT A |
1 | 0 |
0 | 1 |
import cv2
import numpy as np
# Load image (convert to black and white image)
img = cv2.imread('resources/lena.bmp', cv2.IMREAD_GRAYSCALE)
# Check the image size and create a gradient mask of the same size
height, width = img.shape[:2]
mask = np.zeros((height, width), dtype=np.uint8)
cv2.circle(mask, (height//2, width//2), 200, 255, -1)
cv2.imshow('Mask', mask)
# Logical operations
bitwise_and = cv2.bitwise_and(img, mask)
bitwise_or = cv2.bitwise_or(img, mask)
bitwise_xor = cv2.bitwise_xor(img, mask)
bitwise_not = cv2.bitwise_not(img, mask)
cv2.imshow('Original Image', img)
cv2.imshow('Bitwise AND', bitwise_and)
cv2.imshow('Bitwise OR', bitwise_or)
cv2.imshow('Bitwise XOR', bitwise_xor)
cv2.imshow('Bitwise NOT', bitwise_not)
cv2.waitKey(0)
cv2.destroyAllWindows()
3. 결론
이번 글에서는 OpenCV와 Python을 사용하여 이미지 간의 산술 및 논리 연산을 수행하는 방법과 addWeighted()를 이용한 배열 병합을 다루었습니다. 산술 연산을 통해 이미지의 밝기와 대비를 조정하고, 논리 연산을 통해 마스킹 작업을 수행하는 방법과 addWeighted() 함수를 사용해 두 이미지를 가중치를 적용하여 자연스럽게 병합할 수 있음을 확인했습니다. 이러한 기법들은 이미지 처리에서 매우 유용하며, 다양한 편집 및 합성 작업에 쉽게 적용할 수 있습니다.
해당 포스트가 유용하셨다면 하단의 좋아요와 구독하기 부탁드립니다. ^^
★ 모든 내용은 아래의 링크를 참조하였습니다. ★
'강의 (Lecture) > OpenCV 마스터 with Python (초급)' 카테고리의 다른 글
OpenCV + Python 색 공간 변환과 색상 검출 (2) | 2024.10.10 |
---|---|
OpenCV + Python 이미지 자르기 및 크기 조정 (1) | 2024.10.09 |
OpenCV + Python 히스토그램 분석 (1) | 2024.10.07 |
OpenCV + Python 이미지의 밝기와 명암 조절 (0) | 2024.10.05 |
OpenCV + Python 각도 측정기 (실습) (3) | 2024.09.30 |