OpenCV와 자바 - 4.1. 이미지 조작 - 2

2022. 9. 15. 12:23프로그래밍/[Java] OpenCV

728x90

 

그럼 이제 왼쪽 버튼 기능을 활성화 해서 이미지를 조작 해 볼 차례인 것 같습니다.

왼쪽 버튼을 그리는 클래스는 FuncButtonPanel 입니다.

액션 커맨드를 하나씩 붙히고, 리스너를 등록 해 주었습니다.

final class FuncButtonPanel extends JPanel {
	private static final long serialVersionUID = 1101056225940013101L;

	FuncButtonPanel() {
		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);
	}
}
 

각 버튼들은 동일 한 FunctionButtonClickListener 를 등록 했죠.

이 클래스도 역시나 UsrSelectOptions 클래스를 들고 다닐 겁니다.

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

@Override
public void actionPerformed(ActionEvent e) {

	if(usrOptions.isCanvas) imageCanvas.stopTimer();
	else imagePanel.stopTimer();
	
	if (e.getActionCommand().equals("ConvGray")) {
	
		if(usrOptions.isCanvas)
			imageCanvas.loadImage(GRAY_SCALE_DRAWING);
		else
			imagePanel.loadImage(GRAY_SCALE_DRAWING);

	} else if (e.getActionCommand().equals("ConvCanny")) {
		cannyGui();
	} else if (e.getActionCommand().equals("ConvBlackWhite")) {
		if(usrOptions.isCanvas)
			imageCanvas.loadImage(GRAY_SCALE_DRAWING);
		else
			imagePanel.loadImage(BLACK_AND_WHITE_DRAWING);
	} else if (e.getActionCommand().equals("ConvBGR")) {
		if(usrOptions.isCanvas)
			imageCanvas.loadImage(RGBA_DRAWING);
		else
			imagePanel.loadImage(RGBA_DRAWING);
	}
}
 

내용 상으로는 usrOptions을 사용 해서 패널인지 캔버스에 그리고 있는 지를 판단 해서 loadImage 메서드를 호출 하는 것입니다.

이 메서드는 세개가 있습니다. 저번 글에서 언급 했는 데 젤 중요한 게 젤 앞에 있죠 ㅋ

abstract void loadImage(String imagePath, boolean isStop);
abstract void loadImage(int drawingStyle);
abstract void loadImage(int drawingStyle, CannyOption cannyOption);
 

이미지 경로를 받는 메서드는 좀 중요 한대요

public void loadImage(String imagePath, boolean isStop) {
	if(!isStop && StringUtil.isNullOrEmpty(imagePath))
	{
		JOptionPane.showMessageDialog(null, "이미지 경로가 없습니다", "경로 없음",
				JOptionPane.INFORMATION_MESSAGE);
		return;
	}
	else
	{
		if(!isStop && !new File(imagePath).exists())
		{
			JOptionPane.showMessageDialog(null, "이미지 파일을 찾을 수 없습니다 [" + imagePath + "]", "파일 없음",
					JOptionPane.INFORMATION_MESSAGE);
			
			return;
		}
	}
	initMat(imagePath);
	
	// grab a frame every 33 ms (30 frames/sec)
	Runnable frameGrabber = new Runnable() {
		@Override
		public void run() {
			Mat imageMat = grabImage();
			// convert and show the frame
			bufImagew = Utils.matToBufferedImage(imageMat);
			repaint();
		}
	};
	
	scheduleStart(frameGrabber);

	// update the button content
	mThemeButton.setText("UnLoad Image");
}
 

바로 initMat 부분이 있다는 것이죠...

initMat(imagePath);
 

이 메서드는 다음과 같이 구현 되어 있습니다.

BufferedImage 에서 먼저 Mat 클래스를 생성해 놓고 있습니다.

private void initMat(final String imagePath)
{
	// convert and show the frame
	try {
		
		bufImagew = ImageIO.read(new File(imagePath));
		cvMat = Utils.bufferedImageToMat(bufImagew);
		
	} catch (IOException e) {
		System.err.println("Exception during the image elaboration: " + e);
	}
}
 

이렇게 생성 된 Mat 클래스를 계속 재 사용 하게 되는 대요.

최종 적으로 사용 하는 메서드인 grabImage 를 보면,

Mat grabImage(int drawingStyle, CannyOption cannyOpt) {
	Mat matImage = new Mat();
	
	try {
		cvMat.copyTo(matImage);
		
		switch (drawingStyle) {
		case GRAY_SCALE_DRAWING:
			Imgproc.cvtColor(matImage, matImage, Imgproc.COLOR_BGR2GRAY);
			break;
		case CANNY_DRAWING:
			if(cannyOpt !=null)
			{
				CannyFunction.SetCannyLoLimit(cannyOpt.getLowVal());
				CannyFunction.SetCannyHiLimit(cannyOpt.getHighVal());
				CannyFunction.SetInverted(cannyOpt.isInv());
			}
			matImage = CannyFunction.Canny(matImage);
			break;
		case BLACK_AND_WHITE_DRAWING:
			matImage = CannyFunction.BlackAndWhite(matImage, 3);
			break;
		case RGBA_DRAWING:
			// Nothing to do...
			// Imgproc.cvtColor(frame,frame,Imgproc.COLOR_GRAY2BGR);
		}
	} catch (Exception e) {
		// log the error
		System.err.println("Exception during the image elaboration: " + e);
	}

	return matImage;
}
 

생성 된 cvMat 를 사용해서 이미지를 조작 할 때마다 복사해 쓰도록 했습니다.

그렇지 않으면 오류가 나더랍니다~ 삽질을 좀 했네요.

 

그리고 grabImage 이미지 메서드는 이미지 조작의 가장 중요한 메서드 입니다~~

 

원래는 위의 loadImage 이미지 메서드 처럼 쓰레드 상에서 구현 하려고 하였는 데요.

가만 생각 해 보니까.. 연속적인 이미지가 아니라 이미지 하나 쓰는 것이라서 이미지를 계속적으로 읽어 들여서 표출 할 필요가 없더라구요

그리고 Mat 클래스를 재 사용 하면서 별로 의미도 없어지고 말았던 것이죠.

 

 
 

 

 

이미지 하나의 조작에 대해서는 이것으로 마쳐도 될 것 같네요..

다음에는 동영상을 올려서 이미지 조작 할 수 있는 지를 알아 볼 수 있도록 하겠습니다.

 

이상.

 

728x90