밝기값 변환(Intensity Transformation)이란?
입력 영상의 픽셀 값을 사전에 정의한 매핑 관계를 토대로 다른값으로 매핑시키는 것.
Image negatives(이미지 반전, 색상반전)
영상의 intensity level 이 [0, L-1] 사이일 때, 이미지를 반전시키는 것.
s= L-1-R (r: 입력, s : 출력)
이미지의 어두운 영역에서의 흰색 또는 회색 디테일을 찾아내는데 적합하다.
어두운 색상의 이미지를 반전시켜서, 보기 용이하도록 함
Log transformation (로그 변환)
s=c log(1+r) (c: constant(상수), r: 입력, s: 출력)
낮은 intensity 값의 범위가 좁을 경우에, intensity 값을 넓혀줄 때 활용하는데,
이때 어두운 영역(전체적으로 영상이 어두운)에 숨어있는 디테일이나, contrast를 개선시킨다.
contrast : 각각 응접 한 픽셀들 간의 차이.
단순히 입력값에 log를 취해주는 것이 아니라, 1을 더하는 이유?
입력 영상이 가질 수 있는 범위가 보통 0~255인데, log 0 은 고등수학 기준으로 정의되어있지 않다.
따라서 log r로 했을 경우, r=0일 때 오류가 생길 수 있으므로 이를 방지하기 위해 log(1+r)을 쓴다.
log transformation은 단순히 영상을 밝게 해주는 것과는 다르다. 영상을 밝게 하려면 log를 취할 필요 없이 각각의 픽셀에 특정한 상수를 곱해주면 되지만, log transformation은 어두운 영역의 디테일을 살려내는 동시에 오히려 밝은 영역의 디테일은 감소시키는 효과를 보인다.
Power-Law (Gamma) transformation (감마 변환)
𝑠 = 𝑐𝑟^𝛾 ( c: constant(상), r: 입력, s: 출력)
감마(𝛾) 값에 따라서 어두운 범위 이미지의 픽셀이 확장된다.
- 𝛾 = 1 인 경우, 입력값과 출력 값이 같다.
- 𝛾 < 1 인 경우, 어두운 부분의 디테일이 살아난다.
- 𝛾 > 1 인 경우, 밝은 부분의 디테일이 살아난다.
Piecewise-linear transformation functions(부분 선형 변환 함수)
좀 더 복잡한 변환 기능을 형성할 수 있도록, 임의로 함수를 정의하는 것도 가능하다.
원본 영상은 전반적으로 회색조, grayscale 영이 때문에 대부분의 픽셀이 128 근처의 값을 가지고있다.
그러므로 128근처 영역의 값의 디테일을 증가시키도록 함수를 정의한다.
example code
int main() {
Mat image = imread("lena.png", 0);
Mat negative_img = image.clone();
for (int i = 0; i < image.rows; i++)
for (int j = 0; j < image.cols; j++)
negative_img.at<uchar>(i, j) = 255 - image.at<uchar>(i, j); // negativer 구현
imshow("Input image", image);
imshow("Negative transformation", negative_img);
waitKey(0);
return 0;
}
int main() {
Mat image = imread("zx.png", 0); // grayscale
Mat f_img, log_img;
double c = 0.5f; // 상수
image.convertTo(f_img, CV_32F); // log함수 활용하기 위해 float형으로 변환
f_img = abs(f_img) + 1; // 절대값 취해줌. 픽셀값이 0~255이기 때문에 큰 의미는x
log(f_img, f_img);
normalize(f_img, f_img, 0, 255, NORM_MINMAX); // 0~ 255값으로 정규화
convertScaleAbs(f_img, log_img, c); // 픽셀 값에 c값을 곱해서 unsigned 8-bit 타입으로 형 변환시킴
imshow("Input image", image);
imshow("Log transfomation", log_img);
waitKey(0);
return 0;
}
int main() {
Mat image = imread("zx.png", 0); // grayscale
Mat gamma_img;
MatIterator_<uchar> it, end;
float gamma = 0.5;
unsigned char pix[256]; // LUT(룩업테이블) 방식 배열
for (int i = 0; i < 256; i++) {
pix[i] = saturate_cast<uchar>(pow((float)(i / 255.0), gamma)*255.0f); // 감마 값으로 지수승 취해줌
}
gamma_img = image.clone();
for (it = gamma_img.begin<uchar>(), end = gamma_img.end<uchar>(); it != end; it++) {
*it = pix[(*it)]; // 입력 영상의 값들을 다 엑세스하고, 엑세스한 값이 무엇인지에 따라 LUT을 활용함
}
imshow("Input image", image);
imshow("Gamma transfomation", gamma_img);
waitKey(0);
return 0;
}
pix[i] = saturate_cast(pow((float)(i / 255.0), gamma)*255.0f); 부분 해설
gamma transformation은 𝑠 = 𝑐𝑟^𝛾 ( c: constant(상), r: 입력, s: 출력) 공식을 쓰는데, 이때 𝑟= 100, 𝛾 = 10 라 가정하면 출력 s 의 값이 100^10ㅇ로 너무 커진다.
이러한 문제를 해결하기 위해, 입력 r(0~255)을 255로 나누어 0~1 사이의 값으로 만든 후, 𝛾지수 승을 취한 후 다시 255를 곱하는 방법을 사용했다.
이유? 우리는 주어진 영상의 크기 모르고, pow() 함수는 일반적인 사칙연산에 비해서 연산력이 많다.
하지만 아무리 영상의 크기가 크더라해도 0~225 사이일 것이다.
그러므로 각 픽셀마다 특정한 값이 들어왔을 때 이 연산을 수행하는 것이 아니라, 사전에 결과를 만들어놓고 그거를 특정한 픽셀값이 들어올 때마다 array값(여기선 pix[i])을 참조해서 빠르게 연산을 수행하는 방법을 사용하는 것이다.
-> 이런 방식을 룩업 테이블 방식이라고 한다.
룩업 테이블(LUT, Lookup Table)이란?
주어진 연산에 대해 미리 계산된 결과들의 집합(배열)을 가리킨다. 집합(배열)은 주어진 연산에 대한 결과를 계산하는 시간보다 더 빠르게 값을 취득해 갈 수 있도록 사용는 레퍼런스로 사용된다.
즉, LUT는 결과값 가진 배열이고,
배열의 인덱스는 입력값, 배열의 값은 출력값 이다.
이 게시물은 한동대학교 황성수 교수님의 컴퓨터비전 강의를 공부하며 정리한 내용입니다.
'Computer Vision' 카테고리의 다른 글
[OpenCV] 공간 도메인 필터링(Spatial Filtering) (0) | 2020.01.19 |
---|---|
[OpenCV] 히스토그램 평활화(Histogram Equalization) (0) | 2020.01.19 |
[OpenCV] 메모리 관리 및 픽셀 액세스(Memory Management/Pixel Access) (0) | 2020.01.11 |
[OpenCV] Drawing 함수(Drawing Function) (0) | 2020.01.11 |
[OpenCV] Mat 연산자(Mat operator) (0) | 2020.01.11 |