薄建杰 扈桂让
(山西省地震局,山西 太原 030021)
随着当今信息社会各领域的技术的迅猛发展,信息爆炸方式的迅猛增长,种类不断的增加,除了文本、数字、图片外,数码照片作为真相记录、应征已成为不可或缺的重要信息形式之一。数码相机、手机照相功能的便捷使数码相片趋于海量数据成为现实,有时一次重大地震现场科考照片汇集就多达上万张,甚至更多,要从众多照片中找到所需要的照片较为困难,人工一个一个的进行查找,这种方式效率十分低下。快速准确的数码相片查询系统,已成为工作信息提取的必要元素,是信息发布应用的前提,也是当前亟待解决的问题[1-6]。
现代数码相机拍摄的照片就是一种EXIF信息形式,我们通常把英文Exchangeable Image File简称为EXIF,手机拍摄功能更是提供了定位、编辑注释等功能,有了这些信息,建立数据库存放此类信息及对应照片存放地址,从而确立唯一的检索关系。通过设计一个查询界面即可使用户依据查询条件快速找到所需要的内容了[7,8]。
本系统主要功能需求是:1)提取及解读EXIF信息中服务于工作的常用照片信息参数;2)建立EXIF信息数据库,存储EXIF信息及对应照片地址;3)建立查询系统。通过一个查询界面,依据查询条件,查询(读取)指定文件下的所有照片,找到照片后,显示其EXIF信息并在指定区域显示所找到的照片。
本系统设计为B/S三层式结构,数据层为提取出来的EXIF信息库,以SQL Server开发,业务逻辑层为图片处理类,表示层为照片信息查询端。其中逻辑层图片处理类和表示层页面查询端均采用java语言开发,查询端采用C#语言开发,系统运行环境为IIS6.0。
创建及开发数据库:我们要创建exifform表,这个可以依据Access数据库db.mdb。提取出的EXIF信息可以保存成文本文件存放于对应目录中,也可以采用数据库jdbe—odbc桥实现数据的连接查找。
数据库连接采用静态方法加载驱动程序,如使用Java Class类中的forName:连接数据库使用Class.forName("sun.jdbc.odbc.JdbcOdbcDriver")l,需要强调的是要用Connec—tion(java.Sql下属的)类声明一个对象,再用静态方法getConnection创建这个连接,创建前需要类DriverManager调用;然后通过Statement对象并调用Statement来传送并执行。
在做查询界面前,依据EXIF信息字段及数据属性建立数据库及表结构,选择出灾情调查、上报及科考时常用的要查询字段,如时间、位置(即全球定位系统GPS)、图像工具、作者等,然后实现从数据库查询并返回显示的功能。数据库主要字段设置见表1。
表1 数据库主要字段设置
本系统利用数码照片提供的EXIF信息为基础,对照片信息逐个读取并解析。
EXIF实际上是一种图象文件格式,类似JPEG图片存储格式。EXIF格式优势就在JPEG格式头部插入了数码照片的信息,通常以字符串“0xFFD8”作为EXIF标识的JPEG文件头,而字符串“0xFFD9”作为文件尾。把拍摄时的快门、焦距、光圈、ISO、白平衡、日期时间等各种拍摄条件以及相机型号、品牌、拍摄时录制的声音、色彩编码以及缩略图、全球定位系统(GPS)等在文件头中以一系列“0xFF??”格式的字符串表示。可以看出,按照EXIF2.1标准对这些标识符的定义,可以利用任何可以查看JPEG文件的看图软件浏览EXIF格式的照片,检索出它们的具体含义。
我们基于java处理EXIF信息,通过类库Metadata Extractor来直接读取照片(图片)元数据。
也可以采用抽象类,例如图片处理的基类ImageRender(归属于SimpleImage),它通过一个抽象方法render()定义一个抽象类,同时可以引用ImageRender类。ReadRender是所有渲染操作的第一步,它不是一个装饰者,而是可以理解成一个组件,但是其他的子类WriterRender(输出),ScaleRender(缩略处理)均是装饰者。调用最外层的render()方法是通过WriteRender包裹ScaleRender,然后ScaleRender包裹ReadRender,ReadRender需要引入一个输入流,这样处理图像,层层进入。我们可以看到ReadRender首先读取图片数据,通过ScaleRender执行图片缩略,WriteReneder把处理好的图片数据写入输出流中,完成图片信息操作。
对应信息如表2所示。
表2 EXIF信息对应表
EXIF信息提取及保存模块实现:解析EXIF信息,一般用到的Java包是metadata-extractor,这个jar提供了支持获取扩展信息的功能。将照片上传到WEB服务器指定的文件夹下,提取EXIF信息代码主要为:
public class Test1 {
public static void main(String[] args){
File tufilename = new File("F:/图片定位/DCIM/P20130116_125539.jpg");
Metadata metadata;
try {
metadata = JpegMetadataReader.readMetadata(tufilename);
Directory exif = metadata.getDirectory(ExifDirectory.class);
Iterator biaozhi1 = exif.getTagIterator();
while (biaozhi1.hasNext()) {
Tag tag = (Tag)biaozhi1.next();
System.out.println(tag);
}
Directory jpeg = metadata.getDirectory(JpegDirectory.class);
Iterator biaozhi2 = jpeg.getTagIterator();
while (biaozhi2.hasNext()) {
Tag tag = (Tag)biaozhi2.next();
System.out.println(tag);
}
Directory gps = metadata.getDirectory(GpsDirectory.class);
Iterator biaozhi3 = gps.getTagIterator();
while (biaozhi3.hasNext()) {
Tag tag = (Tag)biaozhi3.next();
System.out.println(tag);
}
} catch (JpegProcessingException e) {
e.printStackTrace();
}
}
}
EXIF信息入库代码如下:
public class DbUtil {
private static Connection conn;
private static PreparedStatement ps;
private static ResultSet rs;
private static String odbc_url= "jdbc:odbc:Driver={MicroSoft Access Driver (*.mdb)};DBQ=d:workdb.mdb";
private static String odbc_url= "jdbc:odbc:my_oracle_ODBC";
static{
try {
Map
odbc_driver = map.get("odbc_driver");
odbc_url = map.get("odbc_url");
Class.forName(odbc_driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
定义一个Servlet监听器,并在Web.xml中配置,这样在WEB服务器启动时,会自动启动EXIF信息导入任务。
在Web.xml中的配置
Servlet监听器的代码如下:
public class TaskInit implements ServletContextListener {
private Timer importTimer;
private long pireod = 24 * 60 * 60 * 1000;
public TaskInit() {
importTimer = new Timer();
}
public void contextInitialized(ServletContextEvent arg0) {
// 初次执行时间为明天0点5分,周期为24小时
Calendar mingtian = Calendar.getInstance();
mingtian.set(Calendar.DAY_OF_MONTH,
mingtian.get(Calendar.DAY_OF_MONTH) + 1);
mingtian.set(Calendar.HOUR_OF_DAY, 0);
mingtian.set(Calendar.MINUTE, 5);
mingtian.set(Calendar.SECOND, 0);
importTimer.schedule(new ExifImportTask(), mingtian.getTime(), pireod);
}
public void contextDestroyed(ServletContextEvent arg0) {
importTimer.cancel();
}
查询界面设置经常查询的关键选项,如时间、作者、位置、图像描述或来源等,提交数据后,系统对查询字段进行查询处理,查询显示符合条件的照片信息及缩略图,帮助更精确的锁定所需照片。查询代码如下:
public class CustomQueryAction extends DispatchAction {
public ActionForward choose(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
String sql="select ";
List
HttpSession session = request.getSession();
session.setAttribute("postList", null);//每次查询前清空
String[] checked = request.getParameterValues("checkbox");
for(int i=0;i sql += checked[i]+","; displayList.add(checked[i]); } sql =sql.substring(0, sql.length()-1)+" from V_Employee where 1=1";//从视图中查询数据 System.out.println(sql); session.setAttribute("querySql", sql);//存入session,方便根据不同条件再查询 StaffRepolyDaoImpl staff=new StaffRepolyDaoImpl(); List postList = staff.postExecuteQuery(); session.setAttribute("postList", postList);//查询结果 session.setAttribute("display", displayList);//自定义查询的属性 System.out.println(displayList.size()); return mapping.findForward("query"); } } 本文较全面地介绍了基于Java库提供的metadata-extractor类解析EXIF信息并提取入库,为照片的快速查找建立了便捷途径。符合条件的照片也可一一罗列,在预览框及EXIF参数列表里显示区别,比起一般的EXIF信息查看器,本系统不仅解决了照片按时间或按指定属性分类入库的存储问题,还解决了海量照片的管理及信息提取问题,为防震减灾野外科考研究提供帮助。 参考文献: [1] 普雷斯曼.软件工程:实践者研究方法[M].北京:机械工业出版社,2011. [2] openopen.JHeader读取和修改相片EXIF信息的Java类库[EB/OL].httpwww.openopen.comlibviewopen134658175111 5.html. [3] 蓝 雨.查看EXIF完全掌握拍摄信息[J].电子世界,2006(7):25-27. [4] 杨晓锋.数码图像文件格式标准EXIF解读[J].照相机,2005(5):69-71. [5] 韩晓飞,单 波.Seismic Unix在地质雷达数据处理中的应用[J].山西建筑,2015,41(19):256-258. [6] http://www.sharejs.com/codes/java/8723,2015. [7] 梁永霖.基于J2ME的旅游信息查询系统设计与实现[J].福建电脑,2008(8):143-144. [8] 黎 曜.基于EXIF的图片信息采集管理系统的设计与实现[J].计算机与现代化,2012(9):135-136.5 结语