이미지 처리(Image Processing)에서는 이미지 위에서 다양한 연산을 수행합니다. 더 구체적으로 말하면 연산은 픽셀 단위로 이루어지기 때문에, 이미지의 픽셀 수가 많아질수록 연산에 소요되는 시간도 그만큼 늘어납니다. 따라서 연산 시간을 줄이려면 이미지를 효율적으로 스캔하는 것이 필수적입니다. 이번 글에서는 포인터(pointer)를 활용해 효율적인 이미지 스캔 루프를 구현하는 방법을 알아보겠습니다.
색상 감소(Color Reduction)란?
여기서는 색상 감소 전략을 예로 들어 픽셀 스캔 과정을 살펴보겠습니다. RGB 이미지와 같은 컬러 이미지는 3개의 채널(channel)로 구성되어 있으며, 각 채널은 동일한 수의 픽셀과 대응되는 값을 가집니다. 이때 각 값은 8비트 부호 없는 정수(unsigned char) 형태입니다.
따라서 표현할 수 있는 색상의 총 개수는 256 × 256 × 256 = 16,777,216가지에 달합니다. 이렇게 방대한 색상 수를 줄이기 위해 각 픽셀 값을 일정한 크기의 큐브 단위로 나눌 수 있습니다. 예를 들어 8 × 8 × 8 크기의 큐브로 나누면 표현 가능한 색상 수는 32 × 32 × 32 = 32,768가지로 크게 감소합니다.
색상 수가 줄어들면 시스템의 처리 속도 역시 빨라집니다. 다만 이러한 감소 작업을 수행하려면 모든 픽셀을 하나씩 스캔해야 하므로 상당한 시간이 소요될 수 있습니다. 바로 이런 이유로 효율적인 이미지 스캔 방법이 필요합니다.
아래 프로그램은 OpenCV에서 포인터 방식을 사용해 색상을 줄이는 과정을 보여줍니다.
예제 코드
#include<iostream>
#include<opencv2/highgui/highgui.hpp>
using namespace cv;//cv 네임스페이스 선언
using namespace std;//std 네임스페이스 선언
void reducing_Color(Mat &image, int div=64){ //함수 선언//
int total_rows = image.rows;//행(줄)의 개수 가져오기//
int total_columns = image.cols * image.channels();//한 행당 열의 개수 가져오기//
for (int j = 0; j < total_rows; j++){ //행을 순회하는 for 루프 시작
uchar* data = image.ptr<uchar>(j);
for (int i = 0; i < total_columns; i++){ //열을 순회하는 for 루프 시작//
data[i] = data[i] / div * div + div / 2;//픽셀 처리//
}
}
}
int main() {
Mat image;//이미지 행렬 생성//
image = imread("grapes.jpg");//이미지 불러오기//
namedWindow("Image Window");//창 선언//
reducing_Color(image);//함수 호출//
imshow("Image Window", image);//색상이 줄어든 이미지 출력//
waitKey(0);
return 0;
}코드 핵심 포인트
- image.rows : 이미지의 행(세로) 개수를 반환합니다.
- image.cols * image.channels() : 열 개수에 채널 수를 곱해 한 행의 실제 데이터 요소 수를 구합니다.
- image.ptr<uchar>(j) : j번째 행 데이터의 시작 주소를 가리키는 포인터를 반환하며, 행 단위 순회 시 반복자(iterator) 방식보다 빠른 성능을 제공합니다.
- data[i] / div * div + div / 2 : 픽셀 값을 div 단위로 양자화(quantization)하는 연산입니다. 나눗셈 후 다시 곱해 세부 값을 제거하고, div/2를 더해 해당 구간의 중간값으로 색상을 매핑합니다.
실행 결과
