OpenCV와 자바 - 3. 프로세싱-2

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

728x90

 

 
 

그래서,

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...
}
 

이런 스위치 문이었습니다.

그레이 스케일 경우에는 그냥 COLOR_BGR2GRAY 로 바꾸는 것이 전부 입니다.

참고로, OpenCV 에서는 순서가 RGB가 아니라 BGR 입니다. 만들 때 부터 그렇게 만들었지 않았을까 하는 생각이 드는 데... 별로 궁금하지 않아요~(*^^*)

 

그럼 남은 건 캐니 밖에 없어요

이렇게...(*^^*)

private static final class CannyFunction
{
	/* Canny variables */
	private static int CannyLoLimit = 20;
	private static int CannyHiLimit = 255;
	private static boolean CannyInverted = true;
	
	private static Mat BlackAndWhite(Mat obj, int channels) {
		Mat Bnw3C = new Mat(obj.rows(), obj.cols(), CvType.CV_8UC3);
		Mat BnW1C = new Mat(obj.rows(), obj.cols(), CvType.CV_8UC1);
		
		Imgproc.cvtColor(obj, BnW1C, Imgproc.COLOR_BGR2GRAY);
		if (channels == 3) {
			Imgproc.cvtColor(BnW1C,Bnw3C,Imgproc.COLOR_GRAY2BGR);
			return Bnw3C;
		}
		return BnW1C;
	}
	
	static Mat Canny(Mat obj) {
		Mat BnW1C = new Mat(obj.rows(), obj.cols(), CvType.CV_8UC1);
		
		//Convert Image to B/W first.
		BnW1C = BlackAndWhite(obj, 1);
		
		//Then Apply Canny.
		Imgproc.Canny(BnW1C, BnW1C, CannyLoLimit, CannyHiLimit);
		
		//Just convert the 1C B/W to 3 channel B/W
		Mat Canny3C = new Mat(BnW1C.rows(), BnW1C.cols(), CvType.CV_8UC3);		
		Imgproc.cvtColor(BnW1C,Canny3C,Imgproc.COLOR_GRAY2BGR);
		
		//Then invert the image. Black becomes white, white becomes white. 
		if (CannyInverted) {
			Imgproc.threshold(Canny3C, Canny3C, 0, 255, Imgproc.THRESH_BINARY_INV);
		}
		return Canny3C;
	}
	
	static  void SetCannyLoLimit(int limit) {
		CannyLoLimit = limit;	
	}
	static  void SetCannyHiLimit(int limit) {
		CannyHiLimit = limit;	
	}
	static  void SetInverted(boolean status) {
		CannyInverted = status;	
	}
}
 

임계 값은 low/high 를 색상 값 중에 하나 씩 선택해야 되어야 하는 데요

색상은 알다시피 0~255 중에 하나를 선택 해야 되요

기본 값은 20, 255 이네요.

 

이 임계 값을 바꿀 수 있도록, 메서드를 내 놓았네요

그럼 이 임계 값과 이미지를 반전 할 지 말지를 결정하는 메서드를 쓰기 위해서 코딩을 좀 해 볼까 하는 데요

멋대로 짜 놓거라 많이 고치기는 할 거 같아요

우선 캐니 버튼을 누르면 사용자 입력을 받도록 해야 할 것 같네요

이렇게...(*^O^*)

private void cannyGui()
{

	JPanel panel = new JPanel();
	panel.setLayout(new GridLayout(3,0));
	panel.setBackground(new Color(0, 0, 0));
	panel.setSize(new Dimension(250, 100));
	panel.setPreferredSize(new Dimension(250, 100));
	//panel.setLayout(null);
	
	JTextField txtLowVal = new JTextField();
	//txtLowVal.setText("low val: ");
	txtLowVal.setForeground(new Color(0, 0, 0));
	txtLowVal.setHorizontalAlignment(SwingConstants.CENTER);
	txtLowVal.setFont(new Font("Arial", Font.BOLD, 11));
	txtLowVal.setBounds(0, 0, 250, 32);
	
	JTextField txtHighVal = new JTextField();
	//txtHighVal.setText("high val: ");
	txtHighVal.setForeground(new Color(0, 0, 0));
	txtHighVal.setHorizontalAlignment(SwingConstants.CENTER);
	txtHighVal.setFont(new Font("Arial", Font.BOLD, 11));
	txtHighVal.setBounds(0, 0, 250, 32);
	
	JCheckBox chkIsInv = new JCheckBox();
	chkIsInv.setText("Is image inverted ");
	chkIsInv.setForeground(new Color(0, 0, 0));
	chkIsInv.setHorizontalAlignment(SwingConstants.CENTER);
	chkIsInv.setFont(new Font("Arial", Font.BOLD, 11));
	chkIsInv.setBounds(0, 0, 250, 32);
	
	JPanel lowValPane = new JPanel();
	JLabel lblLow = new JLabel("Low Val: "); 
	lblLow.setForeground(new Color(0, 0, 0));
	lblLow.setHorizontalAlignment(SwingConstants.LEFT);
	lblLow.setFont(new Font("Arial", Font.BOLD, 11));
	
	txtLowVal.setSize(new Dimension(170, 25));
	txtLowVal.setPreferredSize(new Dimension(170, 25));
	
	lowValPane.add(lblLow);
	lowValPane.add(txtLowVal);
	
	JPanel highValPane = new JPanel();
	JLabel lblHigh = new JLabel("High Val: "); 
	lblHigh.setForeground(new Color(0, 0, 0));
	lblHigh.setHorizontalAlignment(SwingConstants.LEFT);
	lblHigh.setFont(new Font("Arial", Font.BOLD, 11));
	
	txtHighVal.setSize(new Dimension(170, 25));
	txtHighVal.setPreferredSize(new Dimension(170, 25));
	
	highValPane.add(lblHigh);
	highValPane.add(txtHighVal);
	
	//panel.add(new JLabel("Low: "));
	panel.add(lowValPane);
	panel.add(highValPane);
	panel.add(chkIsInv);

	UIManager.put("OptionPane.minimumSize",new Dimension(270, 120));

	int result = JOptionPane.showConfirmDialog(HelloCamera.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();
			startCam(mIsCanvasDrawing, CANNY_DRAWING);
		}
		catch(NumberFormatException ne)
		{
			JOptionPane.showMessageDialog(null, "부정확 한 값 입니다. 정확한 값을 넣어 주세요!", "부정확 한 값", JOptionPane.INFORMATION_MESSAGE);
		}
	 }
   }
}
 

위처럼 만들려면 다음을 참고 해야 해요~

좀 더 복잡한 거는?

 

[GUI]자바 스윙 메뉴 윈도우 만들기

오늘은 자바 스윙을 사용해서 메뉴 윈도우를 만들어 보기로 했습니다. 스윙에서는 JMenu와 JMenuItem ...

blog.naver.com

이런 얘기 하고 있으면 나도 내 자신이 참 신기 할 때가 있어요.

정말 알긴 하는 걸까? 하는 자조?

 

그래서 다음 처럼 나올 겁니다

 

그러고 보니 startCam 메서드도 또 하나 만들까요?

그전에 startCam 메서드에 아규먼트가 늘어나니까 다음 처럼 불변 객체 하나를 만들어 줍니다.

final class CannyOption
{
	private final int lowVal;
	private final int highVal;
	private final boolean isInv;
	
	CannyOption(final int lowVal, final int highVal, final boolean isInv)
	{
		this.lowVal = lowVal;
		this.highVal = highVal;
		this.isInv = isInv;
	}

	public int getLowVal() {
		return lowVal;
	}

	public int getHighVal() {
		return highVal;
	}

	public boolean isInv() {
		return isInv;
	}
}
 

그럼 아래 부분의 코드가

if (result == JOptionPane.OK_OPTION) {
	try
	{
		int lowVal = Integer.valueOf(txtLowVal.getText());
		int highVal = Integer.valueOf(txtHighVal.getText());
		boolean isInv = chkIsInv.isSelected();
		startCam(mIsCanvasDrawing, CANNY_DRAWING);
	}
	catch(NumberFormatException ne)
	{
		JOptionPane.showMessageDialog(null, "부정확 한 값 입니다. 정확한 값을 넣어 주세요!", "부정확 한 값", JOptionPane.INFORMATION_MESSAGE);
	}
}
 

다음 처럼 바뀌면 될 거고..

if (result == JOptionPane.OK_OPTION) {
	try {
		int lowVal = Integer.valueOf(txtLowVal.getText());
		int highVal = Integer.valueOf(txtHighVal.getText());
		boolean isInv = chkIsInv.isSelected();
		startCam(mIsCanvasDrawing, CANNY_DRAWING, new CannyOption(lowVal, highVal, isInv));
	} catch (NumberFormatException ne) {
		JOptionPane.showMessageDialog(null, "부정확 한 값 입니다. 정확한 값을 넣어 주세요!", "부정확 한 값",
				JOptionPane.INFORMATION_MESSAGE);
	}
}
 

그럼 우리 startCam 은 이렇게 되겠죠?

이렇게...(*^O^*)

 

protected void startCam(final boolean isCanvas, final int functionId) {
	startCam(isCanvas, functionId, null);
}

protected void startCam(final boolean isCanvas, final int functionId, CannyOption cannyOpt) {
	InnerUtil.stopTimer();

	if (mIsCanvasDrawing) {
		camCavs.startCamera(functionId);
	} else {
		camPane.startCamera(functionId);
	}
}
 

자 그럼 연속적으로 startCamera 메서드도 똑 같은 방식으로 바꾸면 되겠죠?

이렇게...(*^O^*)

protected void startCamera(int drawingStyle) {

	startCamera(drawingStyle, null);
}

protected void startCamera(int drawingStyle, CannyOption cannyOpt) {
	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, cannyOpt);
					// 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();
	}
}
 

마지막으로 grabFrame 이네요..

별 내용은 없어요..

이렇게...(*^O^*)

static Mat grabFrame() {
	return grabFrame(RGBA_DRAWING, null);
}

static Mat grabFrame(int drawingStyle, CannyOption cannyOpt) {
	// 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:
					if(cannyOpt !=null)
					{
						CannyFunction.SetCannyLoLimit(cannyOpt.getLowVal());
						CannyFunction.SetCannyHiLimit(cannyOpt.getHighVal());
						CannyFunction.SetInverted(cannyOpt.isInv());
					}
					frame = CannyFunction.Canny(frame);
					break;
				case BLACK_AND_WHITE_DRAWING:
					frame = CannyFunction.BlackAndWhite(frame, 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 frame;
}
 

그런데 가만히 생각 해 보니 누를 때마다 입력하게 만드는 것은 좀 아닌 것 같네요..

그래서 이렇게...(*^O^*)

if (result == JOptionPane.OK_OPTION) {
	try {
		int lowVal = Integer.valueOf(txtLowVal.getText());
		int highVal = Integer.valueOf(txtHighVal.getText());
		boolean isInv = chkIsInv.isSelected();
		startCam(mIsCanvasDrawing, CANNY_DRAWING, new CannyOption(lowVal, highVal, isInv));
	} catch (NumberFormatException ne) {
		JOptionPane.showMessageDialog(null, "부정확 한 값 입니다. 정확한 값을 넣어 주세요!", "부정확 한 값",
				JOptionPane.INFORMATION_MESSAGE);
	}
}
else
{
	startCam(mIsCanvasDrawing, CANNY_DRAWING);
}
 

else 구문 하나를 끼워 넣었습니다.

 

다 끝났네요~ 별로 한 건 없지만 말이죠...

 

 

영상 한 번 보고 가실께요~~

이상.

 

728x90