두 개의 이미지를 비교하는 방법에 대해 다루고자 합니다.
Deep Learning 방식은 아니고 전통적인 방식을 통해 수행해볼 것입니다.
두 가지 방법을 통해 이미지를 비교해볼 것입니다.
첫 번째 방법은 Histogram 방식, 두 번째 방식은 Subtraction 방식입니다. 흔히 인간이 비교하듯이 완벽하게 두 이미지의 일치율을 판단하는 방식은 아니지만 제한적인 상황에서의 비교를 수행할 때는 꽤 유용한 방식들입니다.
각각의 방식에 대한 내용은 다음과 같습니다.
- Histogram
Image Histogram은 Image에서 밝은 Pixel과 어두운 Pixel의 수가 어느정도 분포하고 있는지를 나타낸 그래프입니다. 여기서는 Image의 HLS를 분리하여 해당 값을 기준으로 Histogram을 생성하고 생성된 두 개의 Histogram을 비교하여 일치도를 계산할 것입니다.
이때 HLS는 Hue, Luminance, Saturation Channel을 가진 Image입니다. Hue는 Color와 같은 의미이며, Satureation은 색깔의 포화도, 즉 흰색이나 회색이 들어있지 않은 색의 순수한 정도 또는 색의 선명도를 나타냅니다. Luminance는 밝기를 의미합니다.
우선, 코드를 작성해봅니다.
double compare_with_histogram(Color32 **targetImage, const char* standardImage, int width, int height) {
// standardImage Name
if (standardImage == NULL) return NULL;
char* res = (char*)malloc(strlen(standardImage) + 1);
strcpy(res, standardImage);
Mat imgsHLS[2];
Mat histogram[2];
int channel_numbers[] = { 0, 1, 2 };
// target image
Mat TargetImage(height, width, CV_8UC4, *targetImage);
resize(TargetImage, TargetImage, Size(480, 270));
cvtColor(TargetImage, TargetImage, COLOR_RGB2BGR);
cvtColor(TargetImage, imgsHLS[0], COLOR_BGR2HLS);
// standard image
Mat StandardImage = imread(res, IMREAD_ANYCOLOR);
resize(StandardImage, StandardImage, Size(480, 270));
cvtColor(StandardImage, imgsHLS[1], COLOR_BGR2HLS);
// calculation
for (int i = 0; i < 2; i++) {
int* number_bins = new int[imgsHLS[i].channels()];
for (int ch = 0; ch < imgsHLS[i].channels(); ch++) {
number_bins[ch] = BINS;
}
float ch_range[] = { 0.0, 255.0 };
const float *chanel_ranges[] = { ch_range, ch_range, ch_range };
// histogram
calcHist(&imgsHLS[i], 1, channel_numbers, Mat(), histogram[i], imgsHLS[i].channels(), number_bins, chanel_ranges);
normalize(histogram[i], histogram[i], 1.0);
}
// result
double matching_score = compareHist(histogram[0], histogram[1], HISTCMP_CORREL);
return matching_score;
}
Standard Image와 Target Image를 HLS Histogram을 기반으로 비교합니다. 두 이미지의 Size와 Channel, Color Format을 동일하게 맞춰주고 각 Channel마다의 Historgram 값을 추출하고 Normalize 하여 일치율을 계산합니다.
해당 비교 코드 결과를 보면 다음과 같습니다.
우선, Test 사용한 이미지들은 다음과 같습니다. 위에서 아래 순서대로 ID는 0, 1, 2입니다.
대략적으로 비슷한 이미지를 판별하긴 하지만, Object의 모양보다도 전체적인 평균을 보기 때문에 비교하고자 하는 Object가 같은 크기의 같은 색을 가진 경우 일치도가 정확하지 않을 수 있습니다.
위 이미지의 하단의 2개의 결과를 보면 Target Image가 그리 다르지 않음에도 결과 값이 다르게 나온 것을 보면 그러하겠죠.
- Subtraction
두 이미지를 Gray Scale로 변경한 후 Binary화 합니다. 이후 절댓값을 기준으로 subtraction을 진행한 후 Mopology를 이용해 이미지를 뭉치로 변환합니다.
이 두 결과 이미지를 Pixel 단위로 비교하여 Difference를 측정하고 최종 결과 값은 Difference값을 두 원본 이미지에서의 Object 크기 값으로 나눈 값으로 합니다. 따라서 1은 두 이미지가 일치한다는 것을 의미하고 0과 가까운 것은 두 이미지가 많이 다르다는 것을 의미하게 됩니다.
위와 같은 내용을 기준으로 작성한 코드는 다음과 같습니다.
우선, Pixel 수를 Count 하기 위한 코드입니다. 이는 마지막에 일치율을 계산할 때 사용됩니다.
int count_whitePixels(Mat _img) {
int i, j, count = 0;
for (i = 0; i < _img.cols; i++)
{
for (j = 0; j < _img.rows; j++)
{
int color = _img.at<uchar>(j, i);
if (color < 100) count++;
}
}
return count;
}
실제 비교 부분 코드는 다음과 같습니다.
double compare_with_subtract(Color32 **targetImage, const char* standardImage, int width, int height) {
// standardImage Name
if (standardImage == NULL) return NULL;
char* res = (char*)malloc(strlen(standardImage) + 1);
strcpy(res, standardImage);
// target image
Mat TargetImage(height, width, CV_8UC4, *targetImage);
resize(TargetImage, TargetImage, Size(480, 270));
cvtColor(TargetImage, TargetImage, COLOR_RGB2BGR);
threshold(TargetImage, TargetImage, 200, 255, THRESH_BINARY);
TargetImage = ~TargetImage;
double img1_count = count_whitePixels(TargetImage);
// standard image
Mat StandardImage = imread(res, IMREAD_ANYCOLOR);
resize(StandardImage, StandardImage, Size(480, 270));
threshold(StandardImage, StandardImage, 200, 255, THRESH_BINARY);
StandardImage = ~StandardImage;
double img2_count = count_whitePixels(StandardImage);
// subtract two images
Mat subtraction;
absdiff(TargetImage, StandardImage, subtraction);
cvtColor(subtraction, subtraction, COLOR_RGB2GRAY);
medianBlur(subtraction, subtraction, 7);
threshold(subtraction, subtraction, 50, 255, THRESH_BINARY);
Mat element = getStructuringElement(MORPH_RECT, Size(7, 7));
morphologyEx(subtraction, subtraction, MORPH_CLOSE, element);
morphologyEx(subtraction, subtraction, MORPH_OPEN, element);
double diff_count = count_whitePixels(subtraction);
double matching_score = (double)((double)diff_count / (double)(img1_count + img2_count));
return matching_score;
}
Target Image와 Standard Image의 크기 및 형식을 일치시켜주고 이진화를 진행합니다. 이후 Subtraction을 진행하고 해당 이미지의 잡음을 제거합니다.
앞서 진행해보았던 Histogram 방식의 문제점은 모양은 비슷하지 않지만 전체적인 부피가 같아 일치 여부를 잘못 판단하는 경우를 보완하기 위해 일치율 계산 방식에서 변화를 조금 줘봤습니다.
위 코드를 적용해서 일치도를 판별한 결과는 다음과 같습니다. (Test에 사용한 이미지는 Histogram 때와 동일합니다.)
Histogram 때보다는 조금은 합리적인 유사도 결과라고 생각합니다. 하지만 결국엔 Pixel의 Position에 따른 유사도 즉, Object의 모양에 따른 판별 방식이 아니기 때문에 한계는 분명 있습니다.
이렇게 Histogram과 Subtraction을 통해 이미지의 유사도를 판별하는 과정을 수행해 보았습니다.
앞서 언급하였듯이 두 방식 모두 Image의 전체 Pixel 개수와 같은, 즉 Object의 모양이나 패턴과는 상관없이 유사도를 계산하는 방식이기 때문에 유사한 것 같지 않아보임에도 유사하다고 판별하는 경우가 많습니다.
제한된 환경, 예를 들면 정해진 이미지들 간의 비교를 수행한다던지, 아주 약간의 변화만 있는 이미지를 비교한다던지, 이러한 환경에서의 유사도 판별에는 사용할 수도 있을 것이지만, 실제 유사도를 판별하기 위해서는 모양에 따른 판단을 포함시켜야 한다고 생각합니다. 결론은 Deep Learning이겠죠...
이번 포스트에서는 이렇게 유사도를 측정해볼 수도 있다에 만족해야 할 것 같습니다.
향후 좋은 성능을 가진 유사도 측정 프로그램을 개발하게 되면 추가적으로 포스팅을 해볼 것입니다.
'Programming > Computer Vision' 카테고리의 다른 글
[SLAM] EuRoC를 이용한 ORB-SLAM3 테스트 (0) | 2023.01.11 |
---|---|
[SLAM] ORB SLAM과 SLAM Dataset에 관한 정리 (0) | 2023.01.11 |
[GAN] EveryBodyDanceNow에 대한 정보 및 수행 (0) | 2023.01.09 |
[ZED2] ZED2 카메라를 Unity에서 사용하는 방법 (0) | 2023.01.05 |
[DeepFake] DeepFake 기술과 간단한 예제 수행 (0) | 2023.01.05 |
댓글