OpenCV와 자바 - 4.3. 카메라 조작

2022. 9. 17. 13:35프로그래밍/[Java] OpenCV

728x90

오늘은 마지막으로 카메라를 조작해 보는 것으로 마무리를 해봐야 할 것 같네요

이미 GUI에 모든 것이 마련 되어 있으므로

위 처럼 선택이 되었을 경우에 동작하도록 만 하면 되지 않을까요?

카메라를 위한 캔버스와 패널을 만들고 액션리스너를 좀 수정 하도록 하면 끝날 것 같네요

이렇게...

private CameraPanel cameraPanel;
private CameraCanvas cameraCanvas;
 

음... 고민을 해 본건데

이렇게 우선 뼈대를 구성 하면 어떨까 하는 생각이네요.

interface CVImageProcessing
{
	void setThemeButton(JButton currentBtn);
	void stopTimer();
	
	void loadImage(String imagePath, boolean isStop);
	void loadImage(int drawingStyle);
	void loadImage(int drawingStyle, CannyOption cannyOption);
	
	void loadMovie(String imagePath, boolean isStop);
	void loadMovie(int drawingStyle);
	void loadMovie(int drawingStyle, CannyOption cannyOption);
	
	void loadCamera(String imagePath, boolean isStop);
	void loadCamera(int drawingStyle);
	void loadCamera(int drawingStyle, CannyOption cannyOption);
}

abstract class CVImageCanvas extends Canvas implements CVImageProcessing
{
	public abstract void setThemeButton(JButton currentBtn);	
	public abstract void stopTimer();
	
	public void loadCamera(String imagePath, boolean isStop) {}
	public void loadCamera(int drawingStyle){}
	public void loadCamera(int drawingStyle, CannyOption cannyOption){}
	
	public void loadMovie(String imagePath, boolean isStop){}
	public void loadMovie(int drawingStyle){}
	public void loadMovie(int drawingStyle, CannyOption cannyOption){}
	
	public abstract void loadImage(String imagePath, boolean isStop);
	public abstract void loadImage(int drawingStyle);
	public abstract void loadImage(int drawingStyle, CannyOption cannyOption);
}

abstract class CVMovieCanvas extends Canvas implements CVImageProcessing
{
	public void loadCamera(String imagePath, boolean isStop) {}
	public void loadCamera(int drawingStyle){}
	public void loadCamera(int drawingStyle, CannyOption cannyOption){}
	
	public void loadImage(String imagePath, boolean isStop){}
	public void loadImage(int drawingStyle){}
	public void loadImage(int drawingStyle, CannyOption cannyOption){}
	
	public abstract void loadMovie(String imagePath, boolean isStop);
	public abstract void loadMovie(int drawingStyle);
	public abstract void loadMovie(int drawingStyle, CannyOption cannyOption);
}

abstract class CVCameraCanvas extends Canvas implements CVImageProcessing
{
	public void loadImage(String imagePath, boolean isStop){}
	public void loadImage(int drawingStyle){}
	public void loadImage(int drawingStyle, CannyOption cannyOption){}
	
	public void loadMovie(String imagePath, boolean isStop){}
	public void loadMovie(int drawingStyle){}
	public void loadMovie(int drawingStyle, CannyOption cannyOption){}
	
	public abstract void loadCamera(String imagePath, boolean isStop);
	public abstract void loadCamera(int drawingStyle);
	public abstract void loadCamera(int drawingStyle, CannyOption cannyOption);
}

abstract class CVImagePanel extends JPanel implements CVImageProcessing
{
	public abstract void setThemeButton(JButton currentBtn);	
	public abstract void stopTimer();
	
	public void loadCamera(String imagePath, boolean isStop) {}
	public void loadCamera(int drawingStyle){}
	public void loadCamera(int drawingStyle, CannyOption cannyOption){}
	
	public void loadMovie(String imagePath, boolean isStop){}
	public void loadMovie(int drawingStyle){}
	public void loadMovie(int drawingStyle, CannyOption cannyOption){}
	
	public abstract void loadImage(String imagePath, boolean isStop);
	public abstract void loadImage(int drawingStyle);
	public abstract void loadImage(int drawingStyle, CannyOption cannyOption);
}

abstract class CVMoviePanel extends JPanel implements CVImageProcessing
{
	public void loadCamera(String imagePath, boolean isStop) {}
	public void loadCamera(int drawingStyle){}
	public void loadCamera(int drawingStyle, CannyOption cannyOption){}
	
	public void loadImage(String imagePath, boolean isStop){}
	public void loadImage(int drawingStyle){}
	public void loadImage(int drawingStyle, CannyOption cannyOption){}
	
	public abstract void loadMovie(String imagePath, boolean isStop);
	public abstract void loadMovie(int drawingStyle);
	public abstract void loadMovie(int drawingStyle, CannyOption cannyOption);
}

abstract class CVCameraPanel extends JPanel implements CVImageProcessing
{
	public void loadImage(String imagePath, boolean isStop){}
	public void loadImage(int drawingStyle){}
	public void loadImage(int drawingStyle, CannyOption cannyOption){}
	
	public void loadMovie(String imagePath, boolean isStop){}
	public void loadMovie(int drawingStyle){}
	public void loadMovie(int drawingStyle, CannyOption cannyOption){}
	
	public abstract void loadCamera(String imagePath, boolean isStop);
	public abstract void loadCamera(int drawingStyle);
	public abstract void loadCamera(int drawingStyle, CannyOption cannyOption);
}
 

그런데 위의 인터페이스를 이용하게 된다면 많은 문제점이 있긴 마련이겠죠?

선언부에서도 다음처럼 명확이 나눠야 되고

private ImagePanel imagePanel;
private CVImageCanvas imageCanvas;

private MoviePanel moviePanel;
private CVMovieCanvas movieCanvas;

private CameraPanel cameraPanel;
private CVCameraCanvas cameraCanvas;
 

각 클래스의 인스턴스에 대한 메서드 또한 사용 시에 명확하게 지정 해야만 합니다.

예를 들면, 다음처럼요...

if(usrOptions.getThemeType().equals("Image"))
{
	if(usrOptions.isCanvas)
		imageCanvas.loadImage(CANNY_DRAWING);
	else
		imagePanel.loadImage(CANNY_DRAWING);
} else if(usrOptions.getThemeType().equals("Movie"))
{
	if(usrOptions.isCanvas)
		movieCanvas.loadMovie(CANNY_DRAWING);
	else
		moviePanel.loadMovie(CANNY_DRAWING);
}
else if(usrOptions.getThemeType().equals("Camera"))
{
	if(usrOptions.isCanvas)
		cameraCanvas.loadCamera(CANNY_DRAWING);
	else
		cameraPanel.loadCamera(CANNY_DRAWING);
}
 
imageCanvas의 loadImage

imagePanel의 loadImage

movieCanvas의 loadMovie

moviePanel의 loadMovie

cameraCanvas의 loadMovie

cameraPanel의 loadMovie

 

는 모두 각각의 기능과 메서드의 이름을 구분 해서 사용 해야만 합니다.

조금 생각해 보면 각 클래스의 load 메서드는 모두 통일 해도 무방 하겠다고 생각이 들기도 합니다.

그건 디자인에 문제이기도 하지만요...

 

다음으로 넘어가서, 버튼 패널을 생성 하기 위한 코드도 다음과 같이 추가 해야 되겠죠...

private void initImagePanel(final UsrSelectOptions usrOptions) {
	setLayout(new BorderLayout());
	btnListener = new MainButtonClickListener(usrOptions);
	btnFuncListener = new FunctionButtonClickListener(usrOptions);
	
	JPanel btnFuncPane = new FuncButtonPanel();
	
	JPanel btnCtrlPane = new ThemeButtonPanel(usrOptions);
	
	if(usrOptions.getThemeType().equals("Image"))
	{
		...
	} else if(usrOptions.getThemeType().equals("Movie"))
	{
		...
	} else if(usrOptions.getThemeType().equals("Camera"))
	{
		if(usrOptions.isCanvas)
		{
			if(usrOptions.isDoubleBuffering)
				cameraCanvas = new CameraCanvasDoubleBuffer();
			else
				cameraCanvas = new CameraCanvasNoDoubleBuffer();
			
			add(cameraCanvas, BorderLayout.CENTER);
		}
		else
		{
			cameraPanel = new CameraPanel();
			add(cameraPanel, BorderLayout.CENTER);
		}
	}
	
	add(btnCtrlPane, BorderLayout.SOUTH);
	add(btnFuncPane, BorderLayout.WEST);
	
}
 

메인 액션 리스너에도 추가 하구요..

class MainButtonClickListener implements ActionListener {
	final UsrSelectOptions usrOptions;
	public MainButtonClickListener(final UsrSelectOptions usrOptions) {
		this.usrOptions = usrOptions;
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		boolean isStop = false;
		System.out.println("[actionPerformed]" +e.getActionCommand());
		if (e.getSource() instanceof JButton) {
			JButton currentBtn = (JButton) e.getSource();
			
			
			if(usrOptions.getThemeType().equals("Image"))
			{
				...
				
			} else if(usrOptions.getThemeType().equals("Movie"))
			{
				...
			} else if(usrOptions.getThemeType().equals("Camera"))
			{
				if(usrOptions.isCanvas)
					cameraCanvas.setThemeButton(currentBtn);
				else
					cameraPanel.setThemeButton(currentBtn);
			}
			
		
			if (currentBtn.getText().startsWith("Stop") || currentBtn.getText().startsWith("Unload")) {
				isStop = true;
			}
			
		}
		
		if(!isStop) 
			if (e.getActionCommand().equals("goCameraCanvas")||e.getActionCommand().equals("goCameraPanel")) {}
			else
				mSelectedFile = selectFileGui(usrOptions.getThemeType());
		
		if (e.getActionCommand().equals("goImageCanvas")) {
			...
		} else if (e.getActionCommand().equals("goCameraCanvas")) {
			cameraCanvas.loadCamera(isStop);
		} else if (e.getActionCommand().equals("goCameraPanel")) {
			cameraPanel.loadCamera(isStop);
		}
	}
	
	...
}
 

실제 기능을 가진 클래스는 모든게 거의 비슷해서 하나만 보면

class CameraPanel extends CVCameraPanel
{
	private BufferedImage bufImagew;
	private JButton mThemeButton;
	private boolean cameraActive = false;
	
	public CameraPanel() {
		super();
	}
	
	@Override
	public void paintComponent(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;			
		Graphics offgc;
		Image offscreen;
		
		if (bufImagew != null) {
			offscreen = createImage(getWidth(), getHeight());
			offgc = offscreen.getGraphics();
			// clear the exposed area
			offgc.setColor(getBackground());
			offgc.drawImage(bufImagew, 0, 0, getWidth(), getHeight(), null);
			offgc.setColor(getBackground());

			g2.drawImage(offscreen, 0, 0, getWidth(), getHeight(), null);
			// invalidate();
		}
	}
	
	public void setThemeButton(final JButton themeButton) {
		mThemeButton = themeButton;
	}
	
	public void loadCamera(boolean isStop) {
		
		if (!this.cameraActive) {
			// start the video capture
			openCamera();
			// is the video stream available?
			if (isMovieOpened()) {
				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 = grabFrame();
						// convert and show the frame
						bufImagew = Utils.matToBufferedImage(frame);
						repaint();
					}
				};
				scheduleStart(frameGrabber);
				
				// update the button content
				mThemeButton.setText("Stop Movie");
			} 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
			mThemeButton.setText("Start Movie");

			// stop the timer
			stopAcquisition();
		}
	}
	
	public void loadCamera(int drawingStyle) {
		if (!this.cameraActive) {
			
			// is the video stream available?
			if (isMovieOpened()) {
				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 = grabFrame(drawingStyle);
						// convert and show the frame
						bufImagew = Utils.matToBufferedImage(frame);
						repaint();
					}
				};
				scheduleStart(frameGrabber);
				
				// update the button content
				mThemeButton.setText("Stop Movie");
			} 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
			mThemeButton.setText("Start Movie");

			// stop the timer
			stopAcquisition();
		}
	}
	
	public void loadCamera(int drawingStyle, CannyOption cannyOpt) {
		if (!this.cameraActive) {
			
			// is the video stream available?
			if (isMovieOpened()) {
				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 = grabFrame(drawingStyle, cannyOpt);
						// convert and show the frame
						bufImagew = Utils.matToBufferedImage(frame);
						repaint();
					}
				};
				scheduleStart(frameGrabber);
				
				// update the button content
				mThemeButton.setText("Stop Movie");
			} 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
			mThemeButton.setText("Start Movie");

			// stop the timer
			stopAcquisition();
		}
	}
	
	// a timer for acquiring the video stream
	private ScheduledExecutorService timer;
	// the OpenCV object that realizes the video capture
	private VideoCapture capture = new VideoCapture();
	// the id of the camera to be usedrh dl
	private int cameraId = 0;

	boolean openCamera() ;

	boolean isMovieOpened() ;

	void scheduleStart(Runnable frameGrabber);

	Mat grabFrame() ;
	
	Mat grabFrame(int drawingStyle);

	Mat grabFrame(int drawingStyle, CannyOption cannyOpt) {
	...
	}

	public void stopTimer() {
		...
	}

	/**
	 * Stop the acquisition from the camera and release all the resources
	 */
	public void stopAcquisition() {
		...
	}
}
 

파일에서 읽어 들이는 부분만 없어졌다는 것과 VideoCapture 클래스에 파일이 아니라 번호가 들어갔다는 것 이외에는 별 다른 부분이 없습니다.

 

그리고 이미지 조작을 위한 왼쪽 패널의 버튼을 위해서 FunctionButtonClickListener

class FunctionButtonClickListener implements ActionListener {
	...

	@Override
	public void actionPerformed(ActionEvent e) {
		...
		else if(usrOptions.getThemeType().equals("Camera"))
		{
			if(usrOptions.isCanvas) cameraCanvas.stopTimer();
			else cameraPanel.stopTimer();
		}
		
		...
		else if(usrOptions.getThemeType().equals("Camera"))
		{
			if (e.getActionCommand().equals("ConvGray")) {
				
				if(usrOptions.isCanvas)
					cameraCanvas.loadCamera(GRAY_SCALE_DRAWING);
				else
					cameraPanel.loadCamera(GRAY_SCALE_DRAWING);

			} else if (e.getActionCommand().equals("ConvCanny")) {
				cannyGui(usrOptions);
			} else if (e.getActionCommand().equals("ConvBlackWhite")) {
				if(usrOptions.isCanvas)
					cameraCanvas.loadCamera(GRAY_SCALE_DRAWING);
				else
					cameraPanel.loadCamera(BLACK_AND_WHITE_DRAWING);
			} else if (e.getActionCommand().equals("ConvBGR")) {
				if(usrOptions.isCanvas)
					cameraCanvas.loadCamera(RGBA_DRAWING);
				else
					cameraPanel.loadCamera(RGBA_DRAWING);
			}
		}
	}
	
	private void cannyGui(final UsrSelectOptions usrOptions) {
		...

		int result = JOptionPane.showConfirmDialog(CVTestPane.this, panel, "Choose Canny Option",
				JOptionPane.OK_CANCEL_OPTION);

		if (result == JOptionPane.OK_OPTION) {
			try {
				int lowVal = Integer.valueOf(txtLowVal.getText());
				int highVal = Integer.valueOf(txtHighVal.getText());
				boolean isInv = chkIsInv.isSelected();
				if(usrOptions.getThemeType().equals("Image"))
				{
					...
				}
				else if(usrOptions.getThemeType().equals("Movie"))
				{
					...
				}
				else if(usrOptions.getThemeType().equals("Camera"))
				{
					if(usrOptions.isCanvas)
					{
						cameraCanvas.stopTimer();
						cameraCanvas.loadMovie(CANNY_DRAWING, new CannyOption(lowVal, highVal, !isInv));
					}
					else
					{
						cameraPanel.stopTimer();
						cameraPanel.loadMovie(CANNY_DRAWING, new CannyOption(lowVal, highVal, !isInv));
					}
				}
			} catch (NumberFormatException ne) {
				JOptionPane.showMessageDialog(null, "부정확 한 값 입니다. 정확한 값을 넣어 주세요!", "부정확 한 값",
						JOptionPane.INFORMATION_MESSAGE);
			}
		}
		else
		{
			...
			else if(usrOptions.getThemeType().equals("Camera"))
			{
				if(usrOptions.isCanvas)
					cameraCanvas.loadCamera(CANNY_DRAWING);
				else
					cameraPanel.loadCamera(CANNY_DRAWING);
			}
		}
	}
}
 
 

 

 

이상.

 

728x90

'프로그래밍 > [Java] OpenCV' 카테고리의 다른 글

[2] OpenCV-JavaGUI  (0) 2024.01.16
[1] OpenCV-JavaGUI  (2) 2024.01.15
OpenCV와 자바 - 4.2. 동영상 조작  (0) 2022.09.16
OpenCV와 자바 - 4.1. 이미지 조작 - 2  (0) 2022.09.15
OpenCV와 자바 - 4.1. 이미지 조작 - 1  (0) 2022.09.14