OpenCV와 자바 - 3. 프로세싱

2022. 9. 11. 12:41프로그래밍/[Java] OpenCV

728x90

 

 

정말 처음에 시작 할 때는 내가 이미지 프로세싱이라는 것이 뭔지 모두 알아내 주마 하고 시작 했었던 때가 있었습니다. (글을 찾아 보니 2017년 정도네요..)

지금 생각해 보는 것이지만...

그게 뭔지 열심히 공부 하지 않을 것이라면 깊이 알 필요 없다 라는 것으로 결론이 났습니다.

 

뭐 아는 것도 잊어 먹는 판국에 ♪(๑ᴖ◡ᴖ๑)♪ ~~

오늘은 카메라가 노트북에 달려 있다는 것을 알았으니...

프로세싱에 도전을 해 볼 생각입니다. 물론 많이 도전 했었죠.. 이번에는 자바로 말이죠.

 

잘 모르니 그냥 어떤 분 소스를 퍼다가 내가 맘대로 하기로 했습니다.

github 에서 받아다 썼는 데... 어딘지 모르겠네요~

 

여하튼 소스는 어차피 맘대로 고쳤으니, 혹시 위치를 알게 되면 업데이트 하기로 하겠습니다.

UI 는 이렇게 구성 할 겁니다. 기존 UI 옆에다가 버튼을 갖다가 붙일 거예요

이렇게( ◜𖥦◝ )

왼쪽에다가 Panel 을 하다 붙여야 겠죠...

이렇게~♪(๑ᴖ◡ᴖ๑)♪ - 영어로는 WEST 네요...

private void initCamPanel(final boolean isCanvas)
{
	JPanel btnPane = new ButtonPanel();
	
	if(!isCanvas)
	{
		camPane.setSize(new Dimension(600, 450));
		camPane.setPreferredSize(new Dimension(600, 450));
		setLayout(new BorderLayout());
		add(camPane, BorderLayout.CENTER);
		add(goCam, BorderLayout.SOUTH);
		add(btnPane, BorderLayout.WEST); // 이렇게 ( ◜𖥦◝ )
		
		goCam.setActionCommand("goCamPanel");
		goCam.addActionListener(btnListener);
	}
	else
	{
		... 생략 ...
	}
}
 

ButtonPanel 은 귀찮으니까 내부에다 만들겁니다.

그래야 소스는 뭐 복잡해 지겠지만, 클래스 변수들을 갖다 쓰는 것도 편하고, 카메라도 접근하기 편하기도 해서 그냥 쓸거예요

final class ButtonPanel extends JPanel {
	private static final long serialVersionUID = 1101056225940013101L;
	
	ButtonPanel()
	{
		setLayout(new GridLayout(4,0));
		setSize(new Dimension(100, 450));
		setPreferredSize(new Dimension(100, 450));
		initButtonPane();
	}
	
	void initButtonPane()
	{
		JButton btnRGB = new JButton("RGB");
		btnRGB.setActionCommand("ConvBGR");
		JButton btnGrayscal = new JButton("Gray scale");
		btnGrayscal.setActionCommand("ConvGray");
		JButton btnBlackWhite = new JButton("Black&White");
		btnBlackWhite.setActionCommand("ConvBlackWhite");
		JButton btnCanny = new JButton("Canny");
		btnCanny.setActionCommand("ConvCanny");
		//Add controls to set up horizontal and vertical gaps
		add(btnRGB);
		add(btnGrayscal);
		add(btnBlackWhite);
		add(btnCanny);
		
		btnRGB.addActionListener(btnFuncListener);
		btnGrayscal.addActionListener(btnFuncListener);
		btnBlackWhite.addActionListener(btnFuncListener);
		btnCanny.addActionListener(btnFuncListener);
	}
}
 

액션리스너를 하나 선언(btnFuncListener)하고 각 버튼마다 이름을 붙혔습니다(setActionCommand).

액션 리스너는... 이렇게 이렇게~♪(๑ᴖ◡ᴖ๑)♪

class FunctionButtonClickListener implements ActionListener {

	@Override
	public void actionPerformed(ActionEvent e) {
		
		if (e.getActionCommand().equals("ConvGray")) {
			startCam(mIsCanvasDrawing, GRAY_SCALE_DRAWING);
		} else if (e.getActionCommand().equals("ConvCanny")) {
			startCam(mIsCanvasDrawing, CANNY_DRAWING);
		}else if (e.getActionCommand().equals("ConvBlackWhite")) {
			startCam(mIsCanvasDrawing, BLACK_AND_WHITE_DRAWING);
		} else if (e.getActionCommand().equals("ConvBGR")) {
			startCam(mIsCanvasDrawing, RGBA_DRAWING);
		}
	}

}
 

잘 만들었네요~(😮ㅋㅋ)

 

GUI 만들 때, 원래는 하단에 go Cam 버튼 하나만 있었기 때문에, 시작 하고 난 후에는 버튼 명을 stop 으로 만들고 stop 버튼을 누르면 모든 게 종료 되는 모양새 였습니다.

 

하단 버튼의 액션에 대한 종료 는 다음과 같이 일어 나게 됩니다.

1. 타이머가 종료 되었는가?

1.1 예 > 카메라를 끈다.

1.2 아니오 > 타이머를 종료 한다 > 카메라를 끈다.

였습니다.

 

왼쪽 버튼들의 액션은 이렇게 생각 해 보았죠.

  1. 타이머가 종료 되었는가?
    1.  예 > 버튼 액션을 진행 한다.
    2. 아니요 > 타이머를 종료 한다 > 버튼 액션을 진행 한다.

입니다.

 

그럼, 타이머는 무슨 동작을 할까요?

  1. 카메라가 켜져 있는가?
    1. 예 > 프레임을 읽어 들인다.
    2. 아니오 > 오류를 출력 한다.

입니다.

 

대충 내용이 끝난네요..그럼 startCam 메서드를 한 번 만들어 보았습니다.

이렇게~♪(๑ᴖ◡ᴖ๑)♪ - 말로 한게 아닌데 입이 아프네요.

protected void startCam(final boolean isCanvas, final int functionId) {
	InnerUtil.stopTimer();
	
	if(mIsCanvasDrawing)
	{
		camCavs.startCamera(functionId);
	}
	else
	{
		camPane.startCamera(functionId);
	}
}
 

startCamera 메서드가 바꼈네요.. 정확히는 바뀐게 아니라 오버로딩 되었습니다(overloading)

하나 더 생겼다는 말이죠...이렇게~♪(๑ᴖ◡ᴖ๑)♪

protected void startCamera(int drawingStyle) {
		if (this.cameraActive) {
			
			// is the video stream available?
			if (InnerUtil.isCamOpened()) {
				cameraActive = true;

				// grab a frame every 33 ms (30 frames/sec)
				Runnable frameGrabber = new Runnable() {

					@Override
					public void run() {
						// effectively grab and process a single frame
						Mat frame = InnerUtil.grabFrame(drawingStyle);
						// convert and show the frame
						bufImagew = Utils.matToBufferedImage(frame);
						//System.out.println("3 " + bufImagew.getWidth() + ":" + bufImagew.getWidth());
						// updateImageView(currentFrame, imageToShow);

						repaint();
						// invalidate();
					}
				};
				InnerUtil.scheduleStart(frameGrabber);
				
				// update the button content
				goCam.setText("Stop Camera");
			} else {
				// log the error
				System.err.println("Impossible to open the camera connection...");
			}
		} else {
			// the camera is not active at this point
			this.cameraActive = false;
			// update again the button content
			goCam.setText("Start Camera");

			// stop the timer
			InnerUtil.stopAcquisition();
		}
	}
}
 

하나 더 있는 startCamera 메서드는 저번 글에서 확인 하시면 됩니다.

그리고 cameraActive 변수가 꼭 있어야 하는 지는 모르겠습니다.

그냥 생각하고 싶지는 않은 걸로~

 

혹자는... 오버로딩이 객체지향 할 때 쓴다더군요. 궁금하시면 알아 보시길 바라며~^^

 

그리고 나면, 가장 중요한 메서드가 하나 나옵니다.

grabFrame 입니다. 이 놈도 오버로딩 되었습니다...

하나 더...이렇게~♪(๑ᴖ◡ᴖ๑)♪

static Mat grabFrame(int drawingStyle) {
	// init everything
	Mat frame = new Mat();

	// check if the capture is open
	if (capture.isOpened()) {
		try {
			// read the current frame
			capture.read(frame);
			
			// if the frame is not empty, process it
			if (!frame.empty()) {
				switch(drawingStyle)
				{
				case GRAY_SCALE_DRAWING:
					Imgproc.cvtColor(frame, frame, Imgproc.COLOR_BGR2GRAY);
					break;
				case CANNY_DRAWING:
					frame = CannyFunction.Canny(frame);
					break;
				case BLACK_AND_WHITE_DRAWING:
					frame = CannyFunction.BlackAndWhite(frame, 3);
					break;
				case RGBA_DRAWING:
					//Nothing to do...
				}
			}
		} catch (Exception e) {
			// log the error
			System.err.println("Exception during the image elaboration: " + e);
		}
	}

	return frame;
}
 

누구나 보듯이 젤 중요 하는 곳은 스위치 케이스 구문 이겠죠?

switch(drawingStyle)
{
case GRAY_SCALE_DRAWING:
	Imgproc.cvtColor(frame, frame, Imgproc.COLOR_BGR2GRAY);
	break;
case CANNY_DRAWING:
	frame = CannyFunction.Canny(frame);
	break;
case BLACK_AND_WHITE_DRAWING:
	frame = CannyFunction.BlackAndWhite(frame, 3);
	break;
case RGBA_DRAWING:
	//Nothing to do...
}
 

 

Mat 오브젝트를 어떻게 다룰 것인가 라는 부분이 들어 있는 곳 입니다.

내 예상이 맞다면? 다음 같은 기능을 기대 해야 하겠죠?

 

첫번째 버튼 - 천연 색(?) 삼다수 - 카메라 화질이 영 별로네요...

두번 째 버튼 - 칙칙한 삼다수

세 번째 버튼: 조화로운 삼다수 - 근데 이건 뭔지 좀 더 알아봐야 할 듯 하네요...

위에 거와 같음...

음...

 

네 번째 버튼 : 캐니 삼다수 - 삼다수 글자가 보이시나요??

 

대충 결과물과 코드에 대해서 정리 해 보았습니다.

 

계속...

 

 

728x90