OpenCV와 자바 - 4.2. 동영상 조작

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

728x90

 

 

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

위에 껄 한꺼번에 만들다 보니, 예상 외로 힘들어서 한 기능 씩 만들어 보기로 했습니다. 오늘은 이미지 표...

blog.naver.com

어제는 이미지를 해 보았으니, 동영상 조작을 해 볼까 합니다.

 

우선 동영상 용 액션 커맨드로 추가 하겠습니다.

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

	ThemeButtonPanel(final UsrSelectOptions usrOptions) {
		setLayout(new GridLayout(1, 3));
		//setSize(new Dimension(100, 450));
		//setPreferredSize(new Dimension(100, 450));
		initButtonPane(usrOptions);
	}

	void initButtonPane(final UsrSelectOptions usrOptions) {
		JButton goImage = new JButton("Go image~");
		JButton goMovie = new JButton("Go Movie~");
		JButton goCam = new JButton("Go Camera~");
		
		if(usrOptions.isCanvas)
		{
			goImage.setActionCommand("goImageCanvas");
			goMovie.setActionCommand("goMovieCanvas");
		}
		else
		{
			goImage.setActionCommand("goImagePanel");
			goMovie.setActionCommand("goMovieCanvas");
			
		}
		
		add(goImage);
		add(goMovie);
		add(goCam);
		
		goImage.addActionListener(btnListener);
	}
}
 

동영상 파일을 로딩 하는 것도 추가 하고요~

class MainButtonClickListener implements ActionListener {
	...

	@Override
	public void actionPerformed(ActionEvent e) {
		...
		else if (e.getActionCommand().equals("goMovieCanvas")) {
			if(!isStop) mSelectedFile = selectFileGui("Movie");
			movieCanvas.loadImage(mSelectedFile, isStop);
		}else if (e.getActionCommand().equals("goMoviePanel")) {
			if(!isStop) mSelectedFile = selectFileGui("Movie");
			moviePanel.loadImage(mSelectedFile, isStop);
		}

	}
}
 

movieCanvas 인스턴스와 moviepanel 인스턴스를 위한 클래스를 만들어야 겠네요

우선 movieCanvas 는 ImageCanvas와 같은 방식으로 만들 생각이예요

대신 메서드 이름이 조금 바뀌겠죠?

이렇게... moviePanel 의 경우에도 행동 즉 메서드는 똑 같을 것이라고도 예상 될 것입니다.

abstract class MovieCanvas extends Canvas
{
	abstract void setThemeButton(JButton currentBtn);
	
	abstract void stopTimer();
	
	abstract void loadMovie(String imagePath, boolean isStop);
	abstract void loadMovie(int drawingStyle);
	abstract void loadMovie(int drawingStyle, CannyOption cannyOption);
}
 

그래서 가만히 생각 해 보니, 캔버스와 패널로 나누어서 코드를 작성하다가

이 둘이 하는 행동 즉 메서드는 똑 같겠다? 라는 생각이 들어요

그래서 이런 행동을 먼저 정희 해 두는 인터페이스를 부모로 두면 될 것이라는 생각이 들었습니다.

그래서 다음 인터페이스를 만들어 보았어요

 

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

그리고 MovieCanvas 는 이렇게 바꿀꺼예요

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

그런다음 해당 하는 행동을 하는 클래스를 만들어야 합니다.

우선, initImagePanel 메서드는 클래스를 추가해서 다음과 같이 만들 수 있을 거예요

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"))
	{
		if(usrOptions.isCanvas)
		{
			if(usrOptions.isDoubleBuffering)
				imageCanvas = new ImageCanvasDoubleBuffer();
			else
				imageCanvas = new ImageCanvasNoDoubleBuffer();
			
			add(imageCanvas, BorderLayout.CENTER);
		}
		else
		{
			imagePanel = new ImagePanel();
			
			add(imagePanel, BorderLayout.CENTER);
		}
	} else if(usrOptions.getThemeType().equals("Movie"))
	{
		if(usrOptions.isCanvas)
		{
			if(usrOptions.isDoubleBuffering)
				movieCanvas = new MovieCanvasDoubleBuffer();
			else
				movieCanvas = new MovieCanvasNoDoubleBuffer();
			
			add(movieCanvas, BorderLayout.CENTER);
		}
		else
		{
			moviePanel = new MoviePanel();
			add(moviePanel, BorderLayout.CENTER);
		}
	}
	
	
	
	add(btnCtrlPane, BorderLayout.SOUTH);
	add(btnFuncPane, BorderLayout.WEST);
	
}
 

차례 차례로 MovieCanvasDoubleBuffer, MovieCanvasNoDoubleBuffer 와 MoviePanel을 만들어 보도록 합니다.  이 클래스들도 사실 별반 내용이 다를 것이 없어서 하나만 본다면 다음과 같은 느낌이 되겠죠?

class MovieCanvasDoubleBuffer extends MovieCanvas {
	private boolean movieActive = false;

	private BufferedImage bufImagew;
	private JButton mThemeButton;
	
	public MovieCanvasDoubleBuffer()
	{
		super();
	}
	
	public void setThemeButton(final JButton themeButton)
	{
		mThemeButton = themeButton;
	}
	
	@Override
	public void update(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;
		Graphics offgc;
		Image offscreen;
		
		if (bufImagew != null) {
			offscreen = createImage(getWidth(), getHeight());
			offgc = offscreen.getGraphics();
			offgc.setColor(getBackground());
			offgc.drawImage(bufImagew, 0, 0, getWidth(), getHeight(), null);
			offgc.setColor(getBackground());

			g2.drawImage(offscreen, 0, 0, getWidth(), getHeight(), null);
		}
	}

	public void loadMovie(String moviePath, boolean isStop) {
		if(!isStop && StringUtil.isNullOrEmpty(moviePath))
		{
			JOptionPane.showMessageDialog(null, "동영상 이름을 찾을 수가 없습니다.[" +moviePath+ "]", "이름없음",
					JOptionPane.INFORMATION_MESSAGE);
			return;
		}
		else
		{
			if(!isStop && !new File(moviePath).exists())
			{
				JOptionPane.showMessageDialog(null, "동영상 경로를 찾을 수가 없습니다. [" + moviePath + "]", "파일 없음.",
						JOptionPane.INFORMATION_MESSAGE);			
				return;
			}
		}
		
		if (!this.movieActive) {
			openMovie(moviePath);
			if (isMovieOpened()) {
				movieActive = true;
				Runnable frameGrabber = new Runnable() {

					@Override
					public void run() {
						Mat frame = grabFrame();
						bufImagew = Utils.matToBufferedImage(frame);
						repaint();
					}
				};
				scheduleStart(frameGrabber);
				mThemeButton.setText("Stop Movie");
			} else {
				System.err.println("Impossible to open the camera connection...");
			}
		} else {
			this.movieActive = false;
			mThemeButton.setText("Start Movie");
			stopAcquisition();
		}
	}
	
	public void loadMovie(int drawingStyle) {
		if (!this.movieActive) {
			if (isMovieOpened()) {
				movieActive = true;
				Runnable frameGrabber = new Runnable() {

					@Override
					public void run() {
						Mat frame = grabFrame(drawingStyle);
						bufImagew = Utils.matToBufferedImage(frame);
						repaint();
					}
				};
				scheduleStart(frameGrabber);
				
				mThemeButton.setText("Stop Movie");
			} else {
				System.err.println("Impossible to open the camera connection...");
			}
		} else {
			this.movieActive = false;
			mThemeButton.setText("Start Movie");
			stopAcquisition();
		}
	}
	
	public void loadMovie(int drawingStyle, CannyOption cannyOpt) {
		if (!this.movieActive) {
			if (isMovieOpened()) {
				movieActive = true;
				Runnable frameGrabber = new Runnable() {

					@Override
					public void run() {
						Mat frame = grabFrame(drawingStyle, cannyOpt);
						bufImagew = Utils.matToBufferedImage(frame);
						repaint();
					}
				};
				scheduleStart(frameGrabber);
				mThemeButton.setText("Stop Movie");
			} else {
				System.err.println("Impossible to open the camera connection...");
			}
		} else {
			this.movieActive = false;
			mThemeButton.setText("Start Movie");

			stopAcquisition();
		}
	}
	
	private ScheduledExecutorService timer;
	private VideoCapture capture = new VideoCapture();

	boolean openMovie(final String moivePath) {
		return capture.open(moivePath);
	}

	boolean isMovieOpened() {
		return capture.isOpened();
	}

	void scheduleStart(Runnable frameGrabber) {
		{
			timer = Executors.newSingleThreadScheduledExecutor();
			timer.scheduleAtFixedRate(frameGrabber, 0, 33, TimeUnit.MILLISECONDS);
		}
	}

	Mat grabFrame() {
		return grabFrame(RGBA_DRAWING, null);
	}
	
	Mat grabFrame(int drawingStyle) {
		return grabFrame(drawingStyle, null);
	}

	Mat grabFrame(int drawingStyle, CannyOption cannyOpt) {
		Mat frame = new Mat();

		if (capture.isOpened()) {
			try {
				capture.read(frame);

				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:
					}
				}
			} catch (Exception e) {
				System.err.println("Exception during the image elaboration: " + e);
			}
		}
		return frame;
	}
	public void stopTimer() {
		movieActive = false;
		if (timer != null && !timer.isShutdown()) {
			try {
				timer.shutdown();
				timer.awaitTermination(33, TimeUnit.MILLISECONDS);
			} catch (InterruptedException e) {
				System.err.println("Exception in stopping the frame capture, trying to release the camera now... " + e);
			}
		}
	}

	public void stopAcquisition() {
		stopTimer();

		if (capture.isOpened()) {
			// release the camera
			capture.release();
		}
	}
}
 

 

그런 다음 실행 봅니다. 결과는 동영상으로 확인 할께요~~

 

 

 

이상.

 

 

728x90