B/S系统集成高拍仪
概述
高拍仪厂家一般都提供B/S系统集成高拍仪的方法,厂商提供的二次开发绝大多数都是基于ActiveX控件,通过JS脚本来控制,功能上受限,只能使用ActiveX控件暴露的接口且无法自定义界面,另外ActiveX控件的使用限制了系统只能使用IE浏览器或基于IE内核的浏览器。本文采用一种基于Java的解决方案,通过Java Web Start技术可以兼容各种浏览器,也可以在非Window系统下使用,缺点是通常会比厂商提供的原生程序需要慢一些,但可以接受。
数据采集
高拍仪本质上可以看做是USB摄像头,由于Java不能直接访问硬件,在Java的世界,访问摄像头必须借助本地库才能实现,这足以让一大部分人止步,所幸的是,有高手为我们开发了这样一个库github: webcam-capture或官网: webcam-capture:
The goal of this project is to allow integrated or USB-connected webcams to be accessed directly
from Java. Using provided libraries users are able to read camera images and detect motion.
Main project consist of several sub projects - the root one, which contains required classes,
build-in webcam driver compatible with Windows, Linux and Mac OS…
这个库以驱动的形式集成了多种访问摄像头的方式,适用于各种场景,使用简单,示例丰富,强烈推荐有需要的同学使用。目前支持以下驱动:
- OpenIMAJ;
- LTI CIVIL;
- Java Media Framework (JMF);
- Freedom for Media in Java (FMJ);
- OpenCV via JavaCV;
- VLC via vlcj;
- V4L via v4l4j;
- GStreamer (0.10.x only) via gstreamer-java;
- FFmpeg;
- MJPEG IP Cameras;
下面是一段功能很丰富的代码示例,来自WebcamViewerExample.java,大家体会一下👍 :
import java.awt.BorderLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.lang.Thread.UncaughtExceptionHandler;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamDiscoveryEvent;
import com.github.sarxos.webcam.WebcamDiscoveryListener;
import com.github.sarxos.webcam.WebcamEvent;
import com.github.sarxos.webcam.WebcamListener;
import com.github.sarxos.webcam.WebcamPanel;
import com.github.sarxos.webcam.WebcamPicker;
import com.github.sarxos.webcam.WebcamResolution;
/**
* Proof of concept of how to handle webcam video stream from Java
*
* @author Bartosz Firyn (SarXos)
*/
public class WebcamViewerExample extends JFrame implements Runnable, WebcamListener, WindowListener, UncaughtExceptionHandler, ItemListener, WebcamDiscoveryListener {
private static final long serialVersionUID = 1L;
private Webcam webcam = null;
private WebcamPanel panel = null;
private WebcamPicker picker = null;
@Override
public void run() {
Webcam.addDiscoveryListener(this);
setTitle("Java Webcam Capture POC");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
addWindowListener(this);
picker = new WebcamPicker();
picker.addItemListener(this);
webcam = picker.getSelectedWebcam();
if (webcam == null) {
System.out.println("No webcams found...");
System.exit(1);
}
webcam.setViewSize(WebcamResolution.VGA.getSize());
webcam.addWebcamListener(WebcamViewerExample.this);
panel = new WebcamPanel(webcam, false);
panel.setFPSDisplayed(true);
add(picker, BorderLayout.NORTH);
add(panel, BorderLayout.CENTER);
pack();
setVisible(true);
Thread t = new Thread() {
@Override
public void run() {
panel.start();
}
};
t.setName("example-starter");
t.setDaemon(true);
t.setUncaughtExceptionHandler(this);
t.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new WebcamViewerExample());
}
@Override
public void webcamOpen(WebcamEvent we) {
System.out.println("webcam open");
}
@Override
public void webcamClosed(WebcamEvent we) {
System.out.println("webcam closed");
}
@Override
public void webcamDisposed(WebcamEvent we) {
System.out.println("webcam disposed");
}
@Override
public void webcamImageObtained(WebcamEvent we) {
// do nothing
}
@Override
public void windowActivated(WindowEvent e) {
}
@Override
public void windowClosed(WindowEvent e) {
webcam.close();
}
@Override
public void windowClosing(WindowEvent e) {
}
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowDeactivated(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
System.out.println("webcam viewer resumed");
panel.resume();
}
@Override
public void windowIconified(WindowEvent e) {
System.out.println("webcam viewer paused");
panel.pause();
}
@Override
public void uncaughtException(Thread t, Throwable e) {
System.err.println(String.format("Exception in thread %s", t.getName()));
e.printStackTrace();
}
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getItem() != webcam) {
if (webcam != null) {
panel.stop();
remove(panel);
webcam.removeWebcamListener(this);
webcam.close();
webcam = (Webcam) e.getItem();
webcam.setViewSize(WebcamResolution.VGA.getSize());
webcam.addWebcamListener(this);
System.out.println("selected " + webcam.getName());
panel = new WebcamPanel(webcam, false);
panel.setFPSDisplayed(true);
add(panel, BorderLayout.CENTER);
pack();
Thread t = new Thread() {
@Override
public void run() {
panel.start();
}
};
t.setName("example-stoper");
t.setDaemon(true);
t.setUncaughtExceptionHandler(this);
t.start();
}
}
}
@Override
public void webcamFound(WebcamDiscoveryEvent event) {
if (picker != null) {
picker.addItem(event.getWebcam());
}
}
@Override
public void webcamGone(WebcamDiscoveryEvent event) {
if (picker != null) {
picker.removeItem(event.getWebcam());
}
}
}
上述代码是基于Swing开发的,数据获取的工作完全可以在此基础上完成。
获得BufferedImage对象并处理
通过Webcam.getImage()
捕获摄像头当前图像,返回值是一个BufferedImage对象,可以对该对象进一步处理,例如通过HttpClient作为附件上传到服务器。
在获得BufferedImage对象之前,可以通过WebcamImageTransformer接口对图像进行预处理,参考How to rotate image from camera with WebcamImageTransformer
选择驱动
前面说了,github: webcam-capture库支持10种驱动,这10种驱动对应不同的访问摄像头的方式,每一种方式都有自己适用的场景,比如V4L4j Driver只能用在Linux操作系统上。这里推荐使用webcam-capture-driver-openimaj驱动。优点是可用于Windows操作系统,速度快(相比较webcam-capture-driver-javacv驱动因为加载了过多的本地库,速度慢且在某些机器上无法使用)。使用时调用Webcam.setDriver(new OpenImajDriver());
即可。有一点需要注意的是,webcam-capture-driver-openimaj驱动只支持有限的分辨率,在OpenImajDevice.java文件中可以看到以下代码:
/**
* Artificial view sizes. I'm really not sure if will fit into other webcams
* but hope that OpenIMAJ can handle this.
*/
private final static Dimension[] DIMENSIONS = new Dimension[] {
new Dimension(176, 144),
new Dimension(320, 240),
new Dimension(352, 288),
new Dimension(640, 400),
new Dimension(640, 480),
new Dimension(1280, 720),
};
最高支持1280*720分辨率,在有些情况下,这样的分辨率是不够的,我手里有台两台高拍仪,一台是良田的,一台是方正的,在这个分辨率下对A4幅面的纸张进行拍照,边缘总是有大约2厘米无法照到。解决方式是改源码,将要高拍仪支持的要设置的分辨率加入到上面的数组中,或者暴露共有方法可以从外部指定DIMENSIONS数组。分辨率直接影响到拍照的效果,分辨率低的话可能无法识别照片中的文字或数字。如果通过webcam.setViewSize(new Dimension(1280, 1280));
指定的分辨率不在上述列表中,则运行时会抛出异常:
version=1.1.2
os.name=Windows 8.1
os.version=6.3
os.arch=amd64
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
176.0,144.0
320.0,240.0
352.0,288.0
640.0,400.0
640.0,480.0
1280.0,720.0
2105.0,1487.0
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Incorrect dimension [1280x1280] possible ones are [176x144] [320x240] [352x288] [640x400] [640x480] [1280x720] [2105x1487]
at com.github.sarxos.webcam.Webcam.setViewSize(Webcam.java:622)
at com.cast514.tools.gaopaiyi.WebcamFrame.run(WebcamFrame.java:248)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:312)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:745)
at java.awt.EventQueue.access$300(EventQueue.java:103)
at java.awt.EventQueue$3.run(EventQueue.java:706)
at java.awt.EventQueue$3.run(EventQueue.java:704)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:715)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
实际情况是,通过webcam.setViewSize
指定的分辨率必须在上述列表中,但实际的分辨率是高拍仪支持的最接近指定值的分辨率。
B/S系统集成
Java Web Start
下面是搬过来的Java Web Start简介:
Java Web Start是帮助客户机端应用程序开发的一个新技术,该技术的独特之处在于将你关心客
户机是如何启动(从Web浏览器或是桌面)中解放出来。并且,该技术提供了一个使Web服务器
能独立发布和更新客户机代码的集合部署方案。
Java Web Start是一个软件技术,它包含了applet的可移植性、Servlet和Java Server Pages(JSP)
的可维护性以及象XML和HTML这样的标记语言的简易性。它是基于Java的应用程
序,允许从标准的Web服务器启动、部署和更新功能完成的Java 2客户机应用程序。
Java Web Start自身是一个Java应用程序,所以该软件是平台独立的,并且支持Java2平台的任
何客户机系统都支持该软件。当客户机应用程序启动时,Java Web Start自动执行更新,在从原
来的高速缓存装入应用程序的同时,从Web下载罪行的版本。Java Web Start还提供了一个Java
应用程序管理器(Java Application Manager)实用程序,即提供了多种选项,如清除下载的应
用程序的高速缓存、指定多种JRE的使用,设置HTTP代理、还允许最终用户组织他们的Java应用
程序。
关于Java Web Start的使用可以参考其他资料,我们的代码虽然是用Java语言写的,但B/S系统并不限于Java,PHP或Asp.net都可以集成。
数据传输
数据传输使用HTTP协议,在Java中,当然使用久经考验的HttpClient组件,具体使用可以参考其他资料。
总结
上面只是简单介绍了一下集成高拍仪的原理,实际应用的时候可能会遇到各种各样的问题,可以在下面的评论中交流。❤️