개발자 블로그

영상처리 강좌 - 5. 이미지 밝기를 조절해보자! 본문

영상처리강좌

영상처리 강좌 - 5. 이미지 밝기를 조절해보자!

로이드.Roid 2016. 1. 3. 21:16
  안녕하세요~
  영상처리 강좌 다섯 번째 시간입니다~!

  오늘 배워볼 내용은 이미지의 밝기를 조절하는 것 입니다. 이미지의 밝기를 밝게도 해보고 어둡게도 해보고 또 색을 반전하는 것 까지 한번 공부해보도록 하겠습니다.



본 강좌를 처음부터 보시려면 아래 링크를 클릭하세요. (새창)
 ☞ 2015/08/10 - [영상처리강좌] - 영상처리 강좌를 시작합니다~!!


  우리는 이미 이전 강의를 통해서 살짝이나마 이미지를 밝게 하는 법에 대해서 배웠습니다. Gray 영상은 픽셀값이 0 ~ 255로 이루어져 있으며, 아래와 같은 색상체계를 가지고 있습니다. 즉, 0은 검정색, 255는 흰색입니다.



  뭐, 이정도야 포토샵 몇 번 써보셨다면 다 알고 계실 것 입니다.(RGB역시 비슷한 구조이고요.)

  결국 영상의 밝기를 밝게 한다는 것은 픽셀의 값을 255에 근접하게 하는 것이고, 영상을 어둡게 만드는 것은 이와 반대로 0에 가깝도록 값을 조절해주면 되는 것 입니다. 
  그렇다면 왜 255인가? 그건 1Byte(8bit)로 표현할 수 있는 최대의 숫자가 255이기 때문입니다.(2^8 = 256 이고, 따라서 0 ~ 255 까지의 범위를 갖습니다.)

  영상의 밝기를 변경할 때 주의할 한가지는 픽셀값이 0 과 255 사이를 벗어나지 않도록 하는것 입니다. 저 범위를 넘어서는 값을 넣을 때는 어떤 결과가 나올지는 한번 실습을 통해서 확인해 보도록 하겠습니다.

  

  자, 그럼 먼저 영상을 밝게 하는 함수입니다.


int fnWritePGM(char* fileNm, PGMImage* img)
{
	FILE* fp;

	fp = fopen(fileNm, "w");
	if(fp == NULL){
		fprintf(stderr, "파일 생성에 실패하였습니다.\n");
		return FALSE;
	}

	fprintf(fp, "%c%c\n", 'P', '2');
	fprintf(fp, "%d %d\n" , img->width, img->height);
	fprintf(fp, "%d\n", 255);

	int tmp;					// int type임에 유의

	for(int i=0; i<img->height; i++){
		for(int j=0; j<img->width; j++){
			tmp = img->pixels[i][j] + 40;	// 픽셀값을 밝게 변화를 줌

			if(tmp > 255){			// overflow 방지
				tmp = 255;
			}

			fprintf(fp, "%d ", tmp);
		}
	}

	fclose(fp);
	
	return TRUE;
}


  "픽셀값을 밝게 변화를 줌""overflow 방지" 라고 주석 달아놓은 부분이 전부 입니다. 이중 for 문으로 이미지 전체 픽셀에 하나하나 접근하면서 픽셀의 값을 일괄적으로 +40해서 저장합니다. 그 밑은 overflow를 방지하기 위한 코드이고요.



 
  영상을 어둡게 하는 것은 +40에서 부호만 바꿔주면 됩니다. 값이야 뭐 당연히 여러분들이 적당한 값으로 변경해주셔도 되고요~! overflow 방지하는 부분은 이제 255를 넘는지가 아니라 0보다 작은 값을 갖게 되는지 underflow 체크를 해주면 됩니다. 소스는 아래와 같습니다.

int fnWritePGM(char* fileNm, PGMImage* img)
{
	FILE* fp;

	fp = fopen(fileNm, "w");
	if(fp == NULL){
		fprintf(stderr, "파일 생성에 실패하였습니다.\n");
		return FALSE;
	}

	fprintf(fp, "%c%c\n", 'P', '2');
	fprintf(fp, "%d %d\n" , img->width, img->height);
	fprintf(fp, "%d\n", 255);

	int tmp;					// int type임에 유의

	for(int i=0; i<img->height; i++){
		for(int j=0; j<img->width; j++){
			tmp = img->pixels[i][j] - 40;	// 픽셀값을 어둡게 변화를 줌

			if(tmp < 0){			// underflow 방지
				tmp = 0;
			}

			fprintf(fp, "%d ", tmp);
		}
	}

	fclose(fp);
	
	return TRUE;
}


  overflow 방지 로직에 대해서 부연설명을 좀 더 하자면, 먼저 밝게 변경하는 경우에, 이미 기존 영상에서 값이 255인 픽셀이 있을 수 있습니다. 그런데 거기에 영상을 밝게 하기위해서 값을 더 더한다면 255를 초과하게 되고, 우리는 원하는 결과를 얻지 못 할 것 입니다. (이 부분은 컴퓨터구조를 공부하시면 자세히 알 수 있습니다. 이 강좌에서 자세히 설명하기는 좀 그렇고,, 그냥 결과만 아래에서 확인해보죠~ㅋ)

  어둡게 할 때 역시 마찬가지 입니다. 기존 영상에도 이미 어두운 부분이 있을 것이고, 만약 픽셀값이 예를들어 10이라고 했을 때, -30을 하게 되면 값은 -20이 되어 버립니다. 결국 이러한 부분 때문에 overflow(또는 underflow)를 체크하는 로직이 반드시 필요한 것 입니다.


  그럼 각각의 결과를 비교해 보도록 하겠습니다. 먼저 밝게 처리한 영상의 결과 화면입니다. 왼쪽에 있는 사진이 원본사진, 오른쪽에 있는 사진이 밝게 처리한 결과 영상입니다.




  다음은 어둡게 처리한 화면입니다.




  밝게 또는 어둡게.. 감이 오시나요?? 뭐,, 픽셀값에 +/- 만 해주는게 전부이기 때문에 어려운 부분은 없습니다.



  그럼 조금 더 응용을 해보겠습니다. 요즘은 디카가 많아서 필름카메라를 많이 쓰지 않지만, 예전 필름카메라의 필름을 보면 색이 거꾸로 되어있죠(네거티브 필름이라고 하나요?). 이러한 효과(색반전, invert)를 한번 줘 보도록 하겠습니다.

 
int fnWritePGM(char* fileNm, PGMImage* img)
{
	FILE* fp;

	fp = fopen(fileNm, "w");
	if(fp == NULL){
		fprintf(stderr, "파일 생성에 실패하였습니다.\n");
		return FALSE;
	}

	fprintf(fp, "%c%c\n", 'P', '2');
	fprintf(fp, "%d %d\n" , img->width, img->height);
	fprintf(fp, "%d\n", 255);

	unsigned char tmp;

	for(int i=0; i<img->height; i++){
		for(int j=0; j<img->width; j++){
			tmp = 255 - img->pixels[i][j];	// 픽셀값 색 반전

			fprintf(fp, "%d ", tmp);
		}
	}

	fclose(fp);
	
	return TRUE;
}


  소스는 오히려 밝게, 어둡게 처리하는 것 보다 더 간단합니다. 연산 후의 픽셀값도 unsigned char 타입 변수의 값 범위를 넘지 않기 때문에 overflow/underflow 체크 로직도 필요 없습니다. 픽셀의 최대값인 255에서 실제 픽셀값을 빼게 되면 가장 어두운 픽셀값인 0은 255가 될 것이고, 가장 밝은 픽셀값인 255는 0으로 검정색이 될 것 입니다.
  
  프로그램의 실행 결과는 아래와 같습니다.





   이제 마지막으로 밝은 부분은 더 밝게, 어두운 부분은 더 어둡게 하는 처리를 해보겠습니다.(이게 포토샵에서 스크린 효과인가요?? 이름이 있을텐데,, 잘 모르겠네요..-_-;; 그냥 고대비효과 라고 해야하나..?) 암튼~ 이런 처리를 하면 사진이 뭔가 좀 더 느낌있어 보입니다. (그냥 제 주관적인 생각입니다..^^)

int fnWritePGM(char* fileNm, PGMImage* img)
{
	FILE* fp;

	fp = fopen(fileNm, "w");
	if(fp == NULL){
		fprintf(stderr, "파일 생성에 실패하였습니다.\n");
		return FALSE;
	}

	fprintf(fp, "%c%c\n", 'P', '2');
	fprintf(fp, "%d %d\n" , img->width, img->height);
	fprintf(fp, "%d\n", 255);

	int tmp;

	for(int i=0; i<img->height; i++){
		for(int j=0; j<img->width; j++){
			
			// 밝은곳은 더 밝게, 어두운 곳은 더 어둡게
			if(img->pixels[i][j] > 125){
				tmp = (img->pixels[i][j] - 125) * 0.3;
				tmp = img->pixels[i][j] + tmp;
			}
			else{
				tmp = (125 - img->pixels[i][j]) * 0.3;
				tmp = img->pixels[i][j] - tmp;
			}


			// overflow, underflow 체크
			if(tmp > 255){
				tmp = 255;
			}
			else if(tmp < 0){
				tmp = 0;
			}


			fprintf(fp, "%d ", tmp);
		}
	}

	fclose(fp);
	
	return TRUE;
}


  요건 소스가 다른 것들에 비해서 조금 복잡한데, 픽셀값 연산을 위한 계산식이 조금 복잡해서 그렇게 보이는 것이지 기본적으로 픽셀의 값을 조절한다는 점에서는 구조상 모두 동일합니다.

  아래는 프로그램의 실행 결과입니다. 어떤가요?? 좀 더 느낌있어 보이나요~?




   좀전에 위에서 overflow 체크를 안했을 경우에는 결과가 어떻게 나올지에 대해서도 실습을 해보겠다고 하였습니다. 아래는 영상을 밝게 처리할 때 overflow 로직을 주석처리 하고 실행한 결과 입니다. 




  원본과 비교해 보시면 대체로 밝은 부분의 픽셀들이 검정색으로 표현되었다는 것을 알 수 있습니다. 밝은 부분의 픽셀값에 추가로 값이 더해지면서 해당 픽셀값이 255를 넘어서 overflow가 발생한 것이죠. 이렇게 overflow 또는 underflow 체크를 안하게되면 원하는 결과를 얻을 수 없습니다. 영상처리에서만 쓰이는것이 아니고 모든 프로그래밍에 있어서 대단히 중요한 부분이므로 반드시 체크해줘야 합니다. (숫자 개념에서의 overflow 뿐만 아니라 문자열 변수에서도 선언해놓은 문자열 변수의 크기를 넘는 문자를 담도록 되어있지는 않는지 등을 꼭 체크해줘야 합니다.)


  이번 강의에서는 영상을 밝게/어둡게 하는 등 픽셀의 값(value)에 기반한 처리에 대해서 배워봤습니다. 지난번 강의에서도 살짝 언급했던 부분이기 때문에 크게 어려운 부분은 없었을 것 같네요. 강의에서 해보았던 효과들 말고 여러분 스스로 특별한 효과를 주는 프로그램을 작성해보시면 이해하는데도 도움이 되고 더 흥미도 갖을 수 있을 것 같네요~.

  다음 강의에서는 이미지를 이동시키는 등 좌표에 기반한 처리에 대해서 알아보도록 하겠습니다.
  혹시 궁금한 부분이나 잘못된 부분은 댓글로 남겨주세요~ 응원의 메시지도 환영입니다~ㅋ
 
  부족한 강의 봐주셔서 대단히 감사합니다.
 
 

이번 강의에 사용된 소스코드를 다운받으시려면 아래 파일을 클릭하세요.



※ 본 포스트에 대한 링크는 가능하지만, 퍼가는 것은 정중하게 사양합니다.



Comments