背景:项目中需要开发以图搜图的功能,查找资料后发现开源框架Lire可以基本做到。悲剧的是Lire基于java应用,没有php接口可以调用,于是想以json+struts2开发个jee项目,做为php端的http请求接口来应用。
首先:下载Lire的demo文件(看源码是可以的,只是涉及到的算法,很多对于我这种理论知识特差的人来说,基本看不懂)
第二:找到demo的入口文件,然后新建一个web项目。将demo项目内java目录下所有的包文件拷入新建项目内的src目录下( net开头的包是Lire的,com的是测试包文件),另外将所有的jar包文件放入到项目中的WebContent/WEB-INF/lib目录下,最后开始配置struts2的相关内容(http://blog.csdn.net/cdy102688/article/details/9738717)
第三:将测试的java文件改为struts2的action对应类,在struts2的解压文件中找到json相关的jar包导入。并在struts.xml中设置json返回值对应的访问action(详细的配置后面给出)
配置后的struts.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<include file="struts-default.xml" />
<package name="default" namespace="/" extends="struts-default">
<action name="imageSearch" class="com.cdy.imagesearch.action.ImgSearchAction"
method="imageSearch">
<result name="success">/index.jsp</result>
</action>
</package>
<package name="jsonInterface" extends="json-default">
<action name="imageSearchJson" class="com.cdy.imagesearch.action.ImgSearchAction"
method="imageSearchJson">
<result type="json">
<param name="root">responseJson</param>
</result>
</action>
</package>
</struts>
imageSearch:测试页面的action
imageSearchJson:返回json数据接口类型的action
转换后的ImgSearchAction文件(因为请求较少,业务处理不复杂。所以简略了封装前台数据的form层,业务处理的service层)
package com.eelly.imagesearch.action;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import net.semanticmetadata.lire.DocumentBuilder;
import net.semanticmetadata.lire.DocumentBuilderFactory;
import net.semanticmetadata.lire.ImageSearchHits;
import net.semanticmetadata.lire.ImageSearcher;
import net.semanticmetadata.lire.ImageSearcherFactory;
import org.apache.lucene.analysis.core.SimpleAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import com.eelly.imagesearch.beans.Goods;
import com.eelly.imagesearch.daoImpl.ImgSearchDaoImpl;
import com.opensymphony.xwork2.ActionSupport;
public class ImgSearchAction extends ActionSupport {
private static final long serialVersionUID = 1L;
// 索引文件存放路径
private static String INDEX_PATH = "E:/index";
// 要索引的图片文件目录
private static String INDEX_FILE_PATH = "E:/image";
// 请求的类型(1:页面测试,2:php接口调用)
private int type=1;
// 查找出的信息ID
public String goods_id_str = "";
// 用于搜索的图片
private File imgFile;
// 用于搜索的图片临时路径
private String imgFileTmpUrl;
// 定义接口返回时的json变量
public Map responseJson;
public Map getResponseJson() {
return responseJson;
}
public void setResponseJson(Map responseJson) {
this.responseJson = responseJson;
}
public String getImgFileTmpUrl() {
return imgFileTmpUrl;
}
public void setImgFileTmpUrl(String imgFileTmpUrl) {
this.imgFileTmpUrl = imgFileTmpUrl;
}
public File getImgFile() {
return imgFile;
}
public void setImgFile(File imgFile) {
this.imgFile = imgFile;
}
public String getGoods_id_str() {
return goods_id_str;
}
public void setGoods_id_str(String goods_id_str) {
this.goods_id_str = goods_id_str;
}
/**
* 图片搜索(页面测入口)
*/
public String imageSearch()
{
return this.deal_search();
}
/**
* 图片搜索(php接口入口)
*/
public String imageSearchJson()
{
return this.deal_search();
}
/**
* 图片搜索处理
*/
public String deal_search()
{
if (imgFile == null)
{
if (imgFileTmpUrl != null && !"".equals(imgFileTmpUrl))
{
imgFile = new File(imgFileTmpUrl);
type = 2;
}
}
if (imgFile != null)
{
ArrayList<Map<String, String>> goods_arr = new ArrayList<Map<String, String>>();
Map<String, Object> goods_map = new HashMap<String, Object>();
try {
// 创建图片文件索引
this.createIndex();
// 打开索引
IndexReader ir = IndexReader.open(FSDirectory.open(new File(INDEX_PATH)));
// 创建一个图片搜索器
ImageSearcher is = ImageSearcherFactory.createFCTHImageSearcher(ir.numDocs());
//搜索图片源
FileInputStream fis = new FileInputStream(imgFile);
BufferedImage bi = ImageIO.read(fis);
//根据上面提供的图片搜索相似的图片
ImageSearchHits ish = is.search(bi, ir);
//显示前匹配度在10以内(相似界限值)的记录(根据匹配度排序)
for (int i = 0; i < ish.length(); i++) {
if (ish.score(i) >= 0.0f && ish.score(i) <= 10.0f)
{
// 将查询到的相似的结果放入map,然后转换成json格式提供接口返回
Map<String, String> goods = new HashMap<String, String>();
goods.put("goods_id", ish.doc(i).getField("goods_id").stringValue());
goods_arr.add(goods);
goods_id_str += ish.doc(i).get(DocumentBuilder.FIELD_NAME_IDENTIFIER)+",";
}
}
if (type == 2)
{
goods_map.put("total_count", goods_arr.size());
goods_map.put("goods_info", goods_arr);
this.setResponseJson(goods_map);
}
// 当查询完成后将用于搜索的文件删除
imgFile.delete();
} catch (Exception e) {
System.out.println("图片搜索出现异常");
}
}
return SUCCESS;
}
/**
* 创建图片库对应的lucence索引文件
* 可以做成定时任务
* @throws Exception
*/
public void createIndex() throws Exception
{
//创建一个合适的文件生成器,Lire针对图像的多种属性有不同的生成器
DocumentBuilder db = DocumentBuilderFactory.getFCTHDocumentBuilder();
IndexWriterConfig iwc = new IndexWriterConfig(
Version.LUCENE_42,new SimpleAnalyzer(Version.LUCENE_42));
IndexWriter iw = new IndexWriter(FSDirectory.open(
new File(INDEX_PATH)), iwc);
iw.deleteAll(); //先清空所有的索引文件
ImgSearchDaoImpl isd = new ImgSearchDaoImpl();
// 获取数据库商品相关图片信息,作为创建图片库索引的源数据
List<Goods> goods = isd.imgSearch();
File parent = new File(INDEX_FILE_PATH);
for (File f : parent.listFiles())
{
// 创建Lucene索引
Document doc = db.createDocument(new FileInputStream(f),f.getName());
// 添加索引字段
String uri=f.getPath();
@SuppressWarnings("deprecation")
Field field=new Field("goods_id","1354985",Field.Store.YES,Field.Index.NO);
doc.add(field);
// 将文件加入索引
iw.addDocument(doc);
}
iw.commit();
iw.close();
}
}
在创建图片索引的方法内涉及到读取数据库中存放的图片信息,所以会在后面将hibernate整合进来。整合了这两个后,会考虑将spring再放进来。目前的代码,正常流程测试是满足的,但毕竟是初版的代码,待改善的地方肯定很多(用到的时候,不妨多看看想想)。