001package net.gdface.sdk.fse;
002
003import java.io.File;
004import java.util.Collection;
005import java.util.HashSet;
006import java.util.Set;
007
008import net.gdface.utils.Assert;
009import net.gdface.utils.Judge;
010import net.gdface.utils.SimpleLog;
011import net.gdface.utils.TimeCostProbe;
012
013/**
014 * 特征码内存搜索引擎(FSE,feature search engine)java接口类(JNI)<br>
015 * 与{@link FseJniBridge}拆分开主要是为了便于生成c header文件<br>
016 * @author guyadong
017 *
018 */
019public abstract class BaseFeatureSearchEngine extends FseJniBridge {
020        protected static final String ENV_FSE = "FSE_HOME";
021        protected static final String PROP_FSE = ENV_FSE.toLowerCase();
022        protected static final String PROP_LIBNAME = "fse_libname";
023        protected static final String FSE_LIBNAME = getFseLibname();
024
025        protected BaseFeatureSearchEngine(){
026        }
027
028        /**
029         * 加载名为{@code libname}的动态库,
030         * 如果{@code libdir}为{@code null}则调用{@link System#loadLibrary(String)}在系统搜索路径下查找动态库,
031         * 否则调用{@link System#load(String)}加载{@code libdir}指定路径下的动态库
032         * @param libdir
033         * @param libname
034         */
035        protected static void loadLibrary(String libdir, String libname) {
036                Assert.notEmpty(libname, "libname");
037                if(null == libdir){
038                        SimpleLog.log("load dynamic library: {}", libname);
039                        System.loadLibrary(libname);
040                }else{
041                        String filename = new File(libdir,System.mapLibraryName(libname)).getAbsolutePath();
042                        SimpleLog.log("load dynamic library: {}", filename);
043                        System.load(filename);
044                }
045        }
046
047        /**
048         * 查找 fse_home变量<br>
049         * 优先在系统变量定义中查找'fse_home',如果找不到,再在环境变量中查找'FSE_HOME'<br>
050         * 如果都找不到则返回{@code null}
051         * @return
052         */
053        protected static final String getFseHome() {
054                String fseHome = System.getProperty(PROP_FSE);
055                if(null==fseHome){
056                        if(null == (fseHome=System.getenv(ENV_FSE))){
057                                SimpleLog.log("UNDEFINED {},"
058                                                + "you can defined the variable by java -D{}=<value> OR set environment variable '{}'",
059                                                PROP_FSE,PROP_FSE,ENV_FSE);                             
060                        }else{
061                                SimpleLog.log("environment variable {}={}",ENV_FSE, fseHome);
062                        }
063                }else{
064                        SimpleLog.log("{}={},defined by java -D<name>=<value>",PROP_FSE, fseHome);
065                }
066                return fseHome;
067        }
068        protected static final String getFseLibname() {
069                String libname = System.getProperty(PROP_LIBNAME);
070                if(null!=libname){
071                        SimpleLog.log("{}={},defined by java -D<name>=<value>", PROP_LIBNAME,libname);
072                }
073                return libname;
074        }
075        /**
076         * 用缺省值初始化
077         */
078        protected static void init(){
079                init(DEFAULT_MODE,DEFAULT_INITIAL_CAPACITY,DEFAULT_LOAD_FACTOR,DEFAULT_OVERBLOCK_CAPACITY);
080        }
081        /**
082         * 特征码{@code code}与数据库中的特征码比对,以相似度降序返回比对结果<br>
083         * 
084         * @param code
085         * @param sim
086         * @param rows
087         * @param imgMD5Set
088         * @param timeCost 
089         * @return {@link CodeBean}数组
090         * @see #searchCode(byte[], double, int, String[])
091         */
092        public static final CodeBean[] searchCode(byte[] code, double sim, int rows, Set<String>imgMD5Set, TimeCostProbe timeCost){
093                if(null!=timeCost){
094                        timeCost.begin();
095                }
096                try{
097                        return searchCode(code, sim, rows, imgMD5Set==null?null:imgMD5Set.toArray(new String[0]));
098                }finally{
099                        if(null!=timeCost){
100                                timeCost.end();
101                        }
102                }
103        }
104
105        /**
106         * 参见 {@link #searchCode(byte[], double, int, Set, TimeCostProbe)}
107         * @param code
108         * @param sim
109         * @param rows
110         * @param imgMD5Set
111         * @param timeCost
112         * @return
113         * @see #searchCode(byte[], double, int, Set, TimeCostProbe)
114         */
115        public static final CodeBean[] searchCode(byte[] code, double sim, int rows, Collection<String>imgMD5Set, TimeCostProbe timeCost){
116                return searchCode(code, sim, rows, imgMD5Set==null?null:new HashSet<String>(imgMD5Set), timeCost);              
117        }
118        
119        /**
120         * @return 动态库已经正确加载则返回false,否则返回false
121         */
122        public static boolean isLibraryLoaded() {
123                return libraryLoaded;
124        }
125
126        /**
127         * 实现{@link FeatureSe}
128         * @author guyadong
129         *
130         */
131        public  static class FeatureSeImpl extends BaseFeatureSeImpl{
132                private final String name;
133                /**
134                 * @param name 封装实现{@link FseJniBridge}的类名,用于区分实例
135                 */
136                public FeatureSeImpl(String name) {
137                        super();
138                        this.name = name;
139                }
140
141                @Override
142                public CodeBean[] searchCode(byte[] code, double sim, int rows) {
143                        return BaseFeatureSearchEngine.searchCode(code, sim, rows, null);
144                }
145
146                @Override
147                public CodeBean getFeature(byte[] featureId) {
148                        return Judge.isEmpty(featureId) ? null : BaseFeatureSearchEngine.getFeature(featureId);
149                }
150
151                @Override
152                public boolean addFeature(byte[] featureId, byte[] feature, String imgMD5) {
153                        return Judge.isEmpty(feature) ? false : BaseFeatureSearchEngine.addFeature(featureId, feature, imgMD5);
154                }
155                @Override
156                public boolean addFeature(byte[] featureId, byte[] feature, long appid) {
157                        return addFeature(featureId, feature, CodeBean.asImgMD5(appid));
158                }
159                @Override
160                public boolean removeFeature(byte[] featureId) {
161                        return Judge.isEmpty(featureId) ? false : BaseFeatureSearchEngine.removeFeature(featureId);
162                }
163                @Override
164                public void clearAll(){
165                        BaseFeatureSearchEngine.clearAll();
166                }
167
168                @Override
169                public int size() {
170                        return BaseFeatureSearchEngine.size();
171                }
172                @Override
173                public String toString() {
174                        StringBuilder builder = new StringBuilder();
175                        builder.append("BaseFeatureSe [jniBridge=").append(name).append("]");
176                        return builder.toString();
177                }
178        };
179                
180}