그래서,
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;
}
}
Canny 함수를 위해서 임계값이 low, high 값이 들어가네요
임계 값은 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);
}
}
}
}
위처럼 만들려면 다음을 참고 해야 해요~
좀 더 복잡한 거는?
이런 얘기 하고 있으면 나도 내 자신이 참 신기 할 때가 있어요.
정말 알긴 하는 걸까? 하는 자조?
그래서 다음 처럼 나올 겁니다
그러고 보니 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 구문 하나를 끼워 넣었습니다.
다 끝났네요~ 별로 한 건 없지만 말이죠...
영상 한 번 보고 가실께요~~
이상.
'프로그래밍 > [Java] OpenCV' 카테고리의 다른 글
OpenCV와 자바 - 4.1. 이미지 조작 - 1 (0) | 2022.09.14 |
---|---|
OpenCV와 자바 - 4. 동영상과 이미지 UI-1 (1) | 2022.09.13 |
OpenCV와 자바 - 3. 프로세싱 (2) | 2022.09.11 |
OpenCV와 자바 - 2. 헬로 Camera (0) | 2022.09.09 |
OpenCV와 자바 - 1. 헬로 OpenCV (3) | 2022.09.08 |