1 opencv_highgui
滑块 (TrackBar)是 OpenCV 动态调参时常用的便捷工具,它依附于窗口而存在。
opencv 提供了支持添加窗口组件的方法,这些方法都在 cv 命名空间下,对应在 Java 项目中的 org.bytedeco.opencv.global.opencv_highgui 类中。
该类中,封装了很多 cv 命名空间中的静态方法:
2 添加滑块:使用 createTrackbar 方法
使用 btyedeco opencv 项目中提供的 createTrackbar() 方法:
代码:
import org.bytedeco.javacpp.IntPointer;
import org.bytedeco.javacpp.Loader;
import org.bytedeco.javacpp.Pointer;
import org.bytedeco.opencv.global.opencv_core;
import org.bytedeco.opencv.global.opencv_highgui;
import org.bytedeco.opencv.global.opencv_imgcodecs;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_highgui.TrackbarCallback;
import org.junit.Test;
import static org.bytedeco.opencv.global.opencv_core.CV_8UC4;
public class TrackBarTest {
static {
Loader.load(org.bytedeco.opencv.opencv_java.class);
}
private String WINDOW_NAME = "trackbar";
private IntPointer alphaVal = new IntPointer(70);
private int position = 0;
private Mat matImg;
private Mat dst = new Mat();
@Test
public void trackTest() throws Throwable{
matImg = opencv_imgcodecs.imread("./images/longmao.jpeg", CV_8UC4);
matImg.convertTo(dst, CV_8UC4, 1, 0);
opencv_highgui.namedWindow(WINDOW_NAME);
opencv_highgui.createTrackbar("tracker", WINDOW_NAME, alphaVal, 100, new TrackbarCallback() {
@Override
public void call(int pos, Pointer userdata) {
if (pos != position) {
position = pos;
double alpha = pos/100.0;
matImg.convertTo(dst, CV_8UC4, alpha, 0);
opencv_highgui.imshow(WINDOW_NAME, dst);
}
}
}, null);
opencv_highgui.imshow(WINDOW_NAME, dst);
opencv_highgui.waitKey();
}
}
createTrackbar 函数用于创建一个可以调整整数值的滑动条,并将滑动条附加到指定的窗口上,使用起来很方便。它一般会和一个回调函数配合使用。
函数原型:
public static native int createTrackbar(@Str String trackbarname, @Str String winname,
IntPointer value, int count,
TrackbarCallback onChange/*=0*/,
Pointer userdata/*=0*/);
- 参数1: 滑动条名称
- 参数2:目标窗口的名称
- 参数3: 一个指向整数的指针,表示滑块的位置,在创建时,滑块的初始位置就是该变量的当前值;
- 参数4:整数值,表示滑块可达的最大值。滑块最小值为0。
- 参数5:滑块变动时的回调函数;
- 参数6:自定义数据;
3 添加滑块:使用 JFrame
使用 org.bytedeco.opencv 中的功能,需要了解 javaCpp 的机制,了解 Pointer 等相关的数据结构和操作。当然,如果不使用 bytedeco 功能,仅使用 org.opencv 模块中的功能,通过 JFrame 也可以实现界面操作。
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Image;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.bytedeco.javacpp.Loader;
import org.junit.Assert;
import org.junit.Test;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Rect;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
public class TrackerTest {
static {
Loader.load(org.bytedeco.opencv.opencv_java.class);
}
private static final int ALPHA_SLIDER_MAX = 100;
private int alphaVal = 0;
private Mat matImgSrc1;
private Mat matImgSrc2;
private Mat matImgDst = new Mat();
private JFrame frame;
private JLabel imgLabel;
private void addComponentsToPane(Container container, Image img) {
// 必须为 BorderLayout 布局
Assert.assertTrue(container.getLayout() instanceof BorderLayout);
// 新建看板: slider 滑块看板
JPanel sliderPanel = new JPanel();
sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS));
sliderPanel.add(new JLabel(String.format("Alpha x %d", ALPHA_SLIDER_MAX)));
// 新增组件:slider 滑块组件
JSlider slider = new JSlider(0, ALPHA_SLIDER_MAX, 0);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
// 添加监听事件
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
alphaVal = source.getValue();
// 更新状态,并重新渲染窗口内容
double alpha = alphaVal / (double) ALPHA_SLIDER_MAX;
double beta = 1.0 - alpha;
// 两张图片,加权融合
Core.addWeighted(matImgSrc1, alpha, matImgSrc2, beta, 0, matImgDst);
Image img = HighGui.toBufferedImage(matImgDst);
imgLabel.setIcon(new ImageIcon(img));
frame.repaint();
}
});
sliderPanel.add(slider);
container.add(sliderPanel, BorderLayout.PAGE_START);
imgLabel = new JLabel(new ImageIcon(img));
container.add(imgLabel, BorderLayout.CENTER);
}
@Test
public void showTest() throws Throwable{
String imagePath1 = "./images/longmao.jpeg";
String imagePath2 = "./images/longmao.jpeg";
matImgSrc1 = Imgcodecs.imread(imagePath1).submat(new Rect(0, 0, 500, 500));
matImgSrc2 = Imgcodecs.imread(imagePath2).submat(new Rect(500, 200, 500, 500));
Assert.assertNotNull(matImgSrc1);
Assert.assertNotNull(matImgSrc2);
// 创建窗口
frame = new JFrame("trackbar");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 将图片添加至窗口
Image img = HighGui.toBufferedImage(matImgSrc2);
addComponentsToPane(frame.getContentPane(), img);
frame.pack();
frame.setVisible(true); // 显示窗口
while (frame.isValid()) {
Thread.sleep(10000);
}
}
}
这里主要是使用 HighGui.toBufferedImage() 方法,将 Mat 类型的图像,转换为 Image 类型,然后在 JFrame 中显示。