머신비전

MFC를 활용한 영상처리 프로그램

다크엔지니어 2020. 2. 4. 21:20
반응형

이미지의 색상 정보 및 여러 Data 를 검사하는 툴을 개발해 보았다. 기간은 6일 정도 소요됬던 것 같다.

 

기능에 대하여 간단히 설명하자면

1. ImageLoad Button : Inspect 하고자 하는 이미지를 불러온다. 단, 불러올 경우 레이블링 처리후 Align 을 잡게 하였다.

2. RGB or Gray Combo Box : 불러올 이미지를 32bit Color or 8bit Gray 에 대하여 전처리를 한다.

3. Set Divide Button : Check Box 의 경우 BOOL 역할을 하며 옆에 두 Edit에 원하는 X, Y Value 를 Set 한다. 단, Align 영역에 대해서만 진행을 해야한다. Divide 영역에 대한 Min Max Sigma 등을 차트에 나타낸다.                                

4. LineProfile Button : Button Click 하면 마우스로 이미지에 드래그를 할 수 있으며 드래그된 픽셀영역의 값을 2차원 그래프 x : Position, y = Intencity 로 나타낸다.                                

5. Data Transform : 차트의 값을 선택된 이미지 상태로 변경 후 검사하게 된다.

아래는 최종적인 프로그램 실행 인터페이스이다.

 

실행 프로그램 모습

 

개발 환경은 VisualStudio 2010 C++ 32bit MFC Interface 이며, NI Library, ChartViewer DLL 등을 사용하였다.

아래 그림을 보면 생성된 클래스는 많지가 않다.. Aurora 플랫폼으로 개발을 진행하지 않았다. (본인 회사에서 표준으로 사용하는 플랫폼이다. 공개 불가X) 그 이유는 개발 시작 당시는 LineProfile 만 제공하는 툴을 만들어 배포할 예정이였기 때문이다.. 그러다 계속 기능이 추가되었다.

더 이상 다양한 기능이 불필요했기 때문에 MVC 패턴 구조로 만들지 않았다.

간단한 Class 구조는 아래와 같다. 

 

생성된 클래스

소스 중 Chart 에 Data 를 생성하는 함수이다. 

for문에 있는 내용은 이미지 영역을 Divide 에 따라 참조 가능한 소스이다.

처음에는 switch case, if elssif 로 코딩하여 소스가 매우 방대해 졌지만 오랜생각 끝에 

효율적인 코딩을 찾았다.. 맨 아래 for 문은 단순 data 취합이다.

 

void CD_ImageData::ImageUniformity(Image *pSrc, int nImageType, int nImageTrans)
{
	int Imagewidth = 0, Imageheight = 0;
    imaqGetImageSize(pSrc, &Imagewidth, &Imageheight);
    if((Imagewidth <= 0) || (Imageheight <= 0))	return;

	Image *pTmp = imaqCreateImage(IMAQ_IMAGE_U8, 4);

	if(nImageType == 1)
	{
		if(nImageTrans >= 0)
		{
			imaqDuplicate(pTmp, pSrc);
		}
		else
		{
			imaqExtractColorPlanes(pSrc, IMAQ_RGB, NULL, pTmp, NULL); // 여기서 한번에 나누면 될듯
		}
	}
	else if(nImageType == 0)
	{
		imaqDuplicate(pTmp, pSrc);
	}
	else
	{
		return;
	}
	
	if((nDivide_X * nDivide_Y)  > (Imagewidth * Imageheight) / 10)
	{
		nDivide_X = sqrt((Imagewidth * Imageheight) / 10.f);
		nDivide_X = sqrt((Imagewidth * Imageheight) / 10.f);
	}
	
	vecROIs.push_back(CRect(0, 0, Imagewidth, Imageheight));

	int pieceX = Imagewidth / nDivide_X;
	int pieceY = Imageheight / nDivide_Y;

	for(int y = 0; y < nDivide_Y; ++y)
	{
		for(int x = 0; x < nDivide_X; ++x)
		{
			int l = pieceX * x;
			int t = pieceY * y;
			int r = l + pieceX;
			int b = t + pieceY;

			vecROIs.push_back(CRect(l, t, r, b));
		}
	}
	
	for(size_t i = 0; i < vecROIs.size(); ++i)
	{
		Rect rect = imaqMakeRect(vecROIs[i].top, vecROIs[i].left, vecROIs[i].Height(), vecROIs[i].Width());

		Image *roi_image = imaqCreateImage(IMAQ_IMAGE_U8, 4);

		imaqSetImageSize(roi_image, rect.width, rect.height);

		imaqCopyRect(roi_image, pTmp, rect, imaqMakePoint(0, 0));

		QuantifyReport *report = imaqQuantify(roi_image, NULL);
		
		double dMaxvalue = report->global.max;
		double dMinvalue = report->global.min;
		double dAvg = report->global.mean;		
		double dUniformity = (dMaxvalue - dMinvalue) / (2 * dAvg) * 100;
		double dRange = dMaxvalue - dMinvalue;
		double dSigma = report->global.stdDev;
		
		UNIFORMITY_INFO strData;
		strData.strNameNumber.Format(_T("%d")  , i);
		strData.strMax.Format(_T("%f")         , dMaxvalue);
		strData.strMin.Format(_T("%f")         , dMinvalue);
		strData.strRange.Format(_T("%f")       , dRange);
		strData.strAverage.Format(_T("%f")     , dAvg);
		strData.strUniformity.Format(_T("%f")  , dUniformity);
		strData.strSigma.Format(_T("%f")       , dSigma);

		m_vecData.push_back(strData);
				
		imaqDispose(report);
		imaqDispose(roi_image);
	}

	imaqDispose(pTmp);
}

소스를 너무 오랜만에 열어서 보면 내가 한게 맞나 라는 생각이 너무 든다...  블로그를 개설하고 처음으로 게시물을 작성해 보는거라 다소 매끄럽지 않을 수도 있지만 점차 나아지길 바라며 오늘은 여기까지 마치겠다.

반응형