3 분 소요

최초 작성일 : 2022-11-30

주요 활용 기술

  1. MFC 프로그램 개발
  2. OpenCV
  3. GigaE 카메라 영상 가져오기

협업 받은 내역

  1. modbus 활용
  2. 아진보드 활용
  3. 검사 알고리즘

업무 내용

Visual Studio 개발 활경에서 UI 개발하였다. 기존의 리눅스 기반에 개발에서 Window 기반으로 개발하니 기분이 좋았다. 여기서 다른 직원들이 들어오면서 영상처리 관련 협업이 추가적으로 들어왔다. 해당 직원이랑 협업이 잘되지 않았다. OpenCV 라이브러리 활용할줄 모르고 그 거대한 라이브러리를 프로그램에 넣은채 개발하게 되었다. 그러다 보니 프로그램이 버젼관리도 어려워지고 서로 업무 내용을 공유하다 보니 혼란도 많이 생겼다. 그 때무터 아 내가 버젼관리하는것 제대로 해야 내 업무를 타인과 공유를 해야하는 생각 이들어 이 이후 프로젝트는 git을 통해 프로젝트 관리하게 시작 되었다.

주요 개발 기술

  1. 쓰레드 관리
#CWinThread* m_pThread1;
# 함수 선언
m_pThread1 = AfxBeginThread(StartThread1, this);

# 쓰레드 함수 실행 되는 위치 
UINT StartThread1(LPVOID pParam)
{
	CValveSeatInspectionDlg* pDlg = (CValveSeatInspectionDlg*)pParam;
	if (pDlg->m_pCamThread1 != NULL) {
		pDlg->OnClickedBtnCapture1(); // 계속 호출할 함수
		pDlg->ShowCamera_1(false);
	}
	pDlg->m_pThread1 = NULL;

	return 1;
}


수정

쓰레드 관리가 제대로 되지 않고 있었다. 심지어 버튼 이벤트를 쓰레드안에 넣어서 나중에 사용하기 어렵게 쓰고 있다. 코드를 일부만 가져왔지만 5000줄 가까이를 한 파일안에 넣어 정리하기도 어렵게 설계되었다. 클래스를 나누어서 기능별로 구별해 개발이 필요하다.

최근에 (볼 타입 검사기 에서 -추후 글 업로드 예정)

 #인자들 추가하여 전달
	ThreadParameter* Thread_Cam_1 = new ThreadParameter;
	Thread_Cam_1->i_PictureControlNumber = PictureControlNumber;
	Thread_Cam_1->Image = Image;
	Thread_Cam_1->param = this;
	Thread_Cam_1->str_SavePath = str_SavePath;
	Thread_Cam_1->str_Folder = folder;

	m_pThread_1 = AfxBeginThread(ThreadTest_1_Parameter, (LPVOID)Thread_Cam_1);

UINT ThreadTest_1_Parameter(LPVOID param)
{
	ThreadParameter* item = (ThreadParameter*)param;
	CSICSANBallTypeCounterBarDlg* pDlg = (CSICSANBallTypeCounterBarDlg*)item->param;

	pDlg->CheckLogic(item->i_PictureControlNumber, item->Image, item->str_SavePath, item->str_Folder);

	return true;
}

  1. ini파일 세팅

ini 읽기

void 클래스명::ReadIniFile()
{
	TCHAR strReadIni1[255] = { 0 };
	CString strPathIni = "C: 경로 .파일명 .ini";

	::GetPrivateProfileString("카메라 설정", "ExposureTime", "0", strReadIni1, 255, strPathIni);
	if (strReadIni1 == "0")
	{
		write_log_file("설정파일에 문제가 있습니다. ExposureTime 확인 바랍니다.");
	}
	else
	{
		m_CamExposureTIme = _ttoi(strReadIni1);
	}

ini파일 쓰기

void 클래스명 ::WriteIniFile()
{
	char IniPath[_MAX_PATH];   //경로 저장하기
	_getcwd(IniPath, _MAX_PATH);
	if (IniPath[strlen(IniPath) - 1] != '\\')
		strcat_s(IniPath, "\\");
	strcat_s(IniPath, "경로");  // 해당 방법은 현재 경로에서 찾는 방법 이용 

	CString text;

	WritePrivateProfileString("큰분류", "세부분류", text, IniPath);
}

설정 파일을 저장하기 위해서 ini파일을 사용하는게 편하다.

  1. GigaE 카메라 세팅

  1. Mat 이미지 MFC Picture Contrl 에 표시

void 클래스명::DisplayImage_Bitbit(cv::Mat& _targetMat, int viewer_index)
{
	Mat mat_frame = _targetMat.clone();

	RECT r;
	if (viewer_index == DISPLAY_IMAGE_ORIGIN_0)
	{
		m_PC_Display_2_1.GetClientRect(&r);
	}

	resize(mat_frame, mat_frame, Size(r.right - r.left, r.bottom - r.top));


	int bpp = 8 * mat_frame.elemSize();
	assert((bpp == 8 || bpp == 24 || bpp == 32));

	int padding = 0;
	//32 bit image is always DWORD aligned because each pixel requires 4 bytes
	if (bpp < 32)
		padding = 4 - (mat_frame.cols % 4);

	if (padding == 4)
		padding = 0;

	int border = 0;
	//32 bit image is always DWORD aligned because each pixel requires 4 bytes
	if (bpp < 32)
	{
		border = 4 - (mat_frame.cols % 4);
	}



	Mat mat_temp;
	if (border > 0 || mat_frame.isContinuous() == false)
	{
		// Adding needed columns on the right (max 3 px)
		cv::copyMakeBorder(mat_frame, mat_temp, 0, 0, 0, border, cv::BORDER_CONSTANT, 0);
	}
	else
	{
		mat_temp = mat_frame;
	}




	cv::Size winSize(r.right, r.bottom);
	CImage cimage_mfc;
	cimage_mfc.Create(winSize.width, winSize.height, 24);


	BITMAPINFO* bitInfo = (BITMAPINFO*)malloc(sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD));
	bitInfo->bmiHeader.biBitCount = bpp;
	bitInfo->bmiHeader.biWidth = mat_temp.cols;
	bitInfo->bmiHeader.biHeight = -mat_temp.rows;
	bitInfo->bmiHeader.biPlanes = 1;
	bitInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bitInfo->bmiHeader.biCompression = BI_RGB;
	bitInfo->bmiHeader.biClrImportant = 0;
	bitInfo->bmiHeader.biClrUsed = 0;
	bitInfo->bmiHeader.biSizeImage = 0;
	bitInfo->bmiHeader.biXPelsPerMeter = 0;
	bitInfo->bmiHeader.biYPelsPerMeter = 0;


	//그레이스케일 인경우 팔레트가 필요
	if (bpp == 8)
	{
		RGBQUAD* palette = bitInfo->bmiColors;
		for (int i = 0; i < 256; i++)
		{
			palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i;
			palette[i].rgbReserved = 0;
		}
	}


	// Image is bigger or smaller than into destination rectangle
	// we use stretch in full rect

	if (mat_temp.cols == winSize.width && mat_temp.rows == winSize.height)
	{
		// source and destination have same size
		// transfer memory block
		// NOTE: the padding border will be shown here. Anyway it will be max 3px width

		SetDIBitsToDevice(cimage_mfc.GetDC(),
			//destination rectangle
			0, 0, winSize.width, winSize.height,
			0, 0, 0, mat_temp.rows,
			mat_temp.data, bitInfo, DIB_RGB_COLORS);
	}
	else
	{
		SetDIBitsToDevice(cimage_mfc.GetDC(),
			//destination rectangle
			0, 0, winSize.width, winSize.height,
			0, 0, 0, mat_temp.rows,
			mat_temp.data, bitInfo, DIB_RGB_COLORS);
	}


	HDC dc;


	if (viewer_index == DISPLAY_IMAGE_ORIGIN_0)
	{
		dc = ::GetDC(m_PC_Display_2_1.m_hWnd);
		cimage_mfc.BitBlt(dc, 0, 0);

		::ReleaseDC(m_PC_Display_2_1.m_hWnd, dc);

	}

	cimage_mfc.ReleaseDC();
	cimage_mfc.Destroy();
}

가장 많이 공들였던 부분이였는데 MFC 에 8의 배수가 아니면 이미지가 깨지는 현상이 있어서 해당 부분을 몰랐을때 원인 파악하느라 힘들었다.

  1. 버튼 이미지 변경

  1. 로그파일 생성 ```

7. 시리얼 연결

```

추가. 검사 알고리즘

댓글남기기