1. 전역 이진화의 문제점

 1) 전역 이진화 : 영상 전체에 대해 동일한 임계값을 사용해 이진화 수행.(Global binarization)

 2) 불균일한 조명 환경에 취약.

 

이전 이진화 코드를 기반

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int t_value = 128;
void on_trackbar_threshold(int, void*);
Mat src, dst;

int main(int argc, char* argv[])
{
	String filename = "neutrophils.png";

	if (argc > 1) {
		filename = argv[1];
	}

	src = imread(filename, IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

	namedWindow("src");
	imshow("src", src);

	namedWindow("dst");
	createTrackbar("Threshold", "dst", &t_value, 255, on_trackbar_threshold);
	on_trackbar_threshold(0, 0); // Call the function to initialize

	waitKey();
}

void on_trackbar_threshold(int, void*)
{
	threshold(src, dst, t_value, 255, THRESH_BINARY);
	imshow("dst", dst);
}

 

cmd에서 원하는 이미지 실행시키기.

 

빌드하여 만들어진 x64에 있는 release 실행파일을 메인에 갖다 놓는다. 그리고 검색창에 C:\coding\opencv\OpenCV_2주차_예제코드\2주차_Day4\threshold  대신  cmd 를 입력한다.

실행할 파일과 원하는 이미지를 입력 하면 아래와 같이 프로그램이 실행되고 윈도우창에 입력한 이미지가 나오게 된다.

테스트 시 조명이 불균형하게 됨에 따라 이진화 할 때도 불균형이 발생한다.

 

불균형한 조명영향 해결법

-> 지역 이진화.

2. 지역 이진화

픽셀 또는 영역마다 다른 임계값을 사용해 이진화 수행하는 기법 (  밝은 부분은 threshold가 올려주고 어두운 부분은 낮춰줌으로써 이진화를 수행하는 방식.)

보통 영상을 특정 크기 영역으로 분할 하여 이진화를 수행하거나 또는 각 픽셀 근방에 윈도우를 설정하고 해당 윈도우에서 임계값을 결정해 이진화를 수행.

rice.png
0.22MB

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int main()
{
	Mat src = imread("rice.png", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image laod failed!" << endl;
		return -1;
	}
	
	Mat dst1;
	threshold(src, dst1, 0, 255, THRESH_BINARY | THRESH_OTSU);  //전체 영상 이진화

	int bw = src.cols / 4;  //block width    128x128
	int bh = src.rows / 4;  // height

	Mat dst2 = Mat::zeros(src.rows, src.cols, CV_8UC1);

	for (int y = 0; y < 4; ++y) {  // 128x128 크기 블록이 세로 방향으로 0~4 까지 진행
		for (int x = 0; x < 4; ++x) {  // 128x128 크기 블록이 가로방향으로 0~4 까지 진행
			Mat src_ = src(Rect(x * bw, y * bh, bw, bh));//  clone 할 필요없이 단순 참조 했음..  // 입력영상에서 부분영상 추출.   현 x좌표에 block width 크기, y 좌표의 block width 크기  [블록 좌측상단 시작점 좌표]   
			Mat dst_ = dst2(Rect(x * bw, y * bh, bw, bh)); //dst2의 참조된 부분영상을 dst_ 에 저장.
			threshold(src_, dst_, 0, 255, THRESH_BINARY | THRESH_OTSU); // 입력,출력 영상 부분 영상 참조로 받아서 dst_ 가 업데이트 됨에 따라 dst2 부분영상도  업데이트 됨
			// 16개 부분 영상 이진화
		}
	}

	imshow("dst1", dst1);
	imshow("dst2", dst2);
	waitKey(); 
}

 

전체 영상 이진화에 비해 부분이진화가 더 정확히 이진화가 된 걸 확인할 수 있다.

지역 이진화는 opencv 함수에서 지원하지 않기에 위처럼 직접 구현하였다. 

3. 적응형 이진화

opencv에서는 adaptiveThreshold 함수를 제공함으로써 지역 이진화의 일부기능을 수행할 수있다.

void adaptiveThreshold(InputArray src, OutputArray dst, double maxValue, int adaptiveMethod,
int thresholdType, int blockSize, double C);

dst: 출력 영상, thresholdType이 THRESH_BINARY인 경우, 아래 수식 이용.  ( Gray Scale 영상)

1) dst(x, y) =  maxValue    if src(x, y)  > T(x, y)

2) dst(x, y) = 0   otherwise

 

maxValue: 이진화에서 사용할 최댓값. (특별한 이유없을시 255 지정)

adaptiveMethod: 블록 평균 계산 방식 지정. 

ADAPTIVE_THRESH_MEAN_C 산술평균
ADAPTIVE_THRESH_GAUSSIAN_C 가우시안 가중치 평균 (  산술평균보다 이것을 사용하는게 나음)

thresholdType: THRESH_BINARY 또는 THRESH_BINARY_INV

blockSize:  사용할 블록 크기. 3이상의 홀수(3,5,7...)

C: 블록 내 평균값 또는 가중 평균값에서 뺄 값.

     (x, y) 픽셀의 임계값으로 T(x, y) = u_B(x,y) -C 를 사용

 

결과 값  , T(x, y) 통해 

1) dst(x, y) =  maxValue    if src(x, y)  > T(x, y)

2) dst(x, y) = 0   otherwise

이진화 수행.

sudoku.jpg
0.04MB

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int block_size = 51;
Mat src, dst;

void on_trackbar(int, void*) {
	int bsize = block_size;  // 사용자 지정 사이즈

	//https://blog.naver.com/PostView.naver?blogId=tipsware&logNo=221251220052&parentCategoryNo=&categoryNo=83&viewDate=&isShowPopularPosts=false&from=postView
	//
	// bsize & 0x00000001 == 1 이면 홀수  0이면 짝수.
	if ((bsize & 0x00000001) == 0) bsize--;  // bsize값이 홀수 가아니라면, bsize --.      홀수 %2!=1 대체, 비트연산으로 조금 속도 빠름.
	if (bsize < 3) bsize = 3;

	adaptiveThreshold(src, dst, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, bsize, 5); // 사용자가 지정한 bsize 를 이용.  현재 bsize에 5를 뺀값을 기준으로 이진화 수행.
	imshow("dst", dst);
}


int main()
{
    src = imread("sudoku.jpg", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image laod failed!" << endl;
		return -1;
	}
	namedWindow("src");
	namedWindow("dst");
	//트랙바 이용해 블록 크기 지정
	createTrackbar("Block Size", "dst", &block_size, 201, on_trackbar);
	on_trackbar(0, 0); // Call the function to initialize
	imshow("src", src);
	waitKey();

}

 

 

단순 이진화보다  깔끔하게 잘 된 모습이다.

block size에 따라 이진화 퀄리티가 달라짐.

너무 키우면 연산량이 늘어나기에 적합한 block size를 선정하자.

 

blocksize, window 개념(지역이진화)  관련해서 다시보자.

'프로그래머스 > OPENCV' 카테고리의 다른 글

레이블링과 외곽선 검출  (0) 2022.12.08
모폴로지 (잡음 제거)  (0) 2022.12.08
컬러영상 처리 기초 (이진화 게시글과 swap됨 )  (0) 2022.12.08
코너 검출 기법  (2) 2022.12.08
허프변환 알고리즘  (0) 2022.12.08

+ Recent posts