package tv.dyndns.kishibe.qmaclone.server.handwriting;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import com.google.common.base.Joiner;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.sun.jna.Pointer;
public class RecognizerZinnia implements Recognizable {
private static final int SIZE = 300;
private static final Object STATIC_KEY = new Object();
private static final Logger logger = Logger.getLogger(RecognizerZinnia.class.toString());
private final LoadingCache<Object, String> availableCharacters = CacheBuilder.newBuilder()
.concurrencyLevel(1).build(new CacheLoader<Object, String>() {
@Override
public String load(Object arg0) throws Exception {
return loadAvailableCharacters();
}
});
// recognizerのインスタンスはObjectPoolに保持させる
private final ZinniaLibrary zinnia;
private final ObjectPool<Pointer> pool;
@Inject
public RecognizerZinnia(ZinniaLibrary zinnia, ZinniaObjectFactory zinniaObjectFactory) {
this.zinnia = zinnia;
this.pool = new GenericObjectPool<Pointer>(zinniaObjectFactory);
}
private String loadAvailableCharacters() throws NoSuchElementException, IllegalStateException,
Exception {
Pointer recognizer = null;
try {
recognizer = (Pointer) pool.borrowObject();
int sizeOfRecognizer = (int) zinnia.zinnia_recognizer_size(recognizer);
Set<String> characters = Sets.newTreeSet();
for (int i = 0; i < sizeOfRecognizer; ++i) {
characters.add(zinnia.zinnia_recognizer_value(recognizer, i));
}
return Joiner.on("").join(characters);
} finally {
if (recognizer != null) {
pool.returnObject(recognizer);
}
}
}
@Override
public String[] recognize(double[][][] inputStrokes) {
Pointer character = zinnia.zinnia_character_new();
zinnia.zinnia_character_clear(character);
zinnia.zinnia_character_set_width(character, SIZE);
zinnia.zinnia_character_set_height(character, SIZE);
int strokeIndex = 0;
for (double[][] stroke : inputStrokes) {
for (double[] p : stroke) {
int x = (int) (p[0] * SIZE + 0.5);
int y = (int) (p[1] * SIZE + 0.5);
zinnia.zinnia_character_add(character, strokeIndex, x, y);
}
++strokeIndex;
}
Pointer recognizer = null;
Pointer result = null;
try {
recognizer = (Pointer) pool.borrowObject();
result = zinnia.zinnia_recognizer_classify(recognizer, character, 12);
if (result == null) {
System.err.printf("%s\n", zinnia.zinnia_recognizer_strerror(recognizer));
return null;
}
List<String> values = new ArrayList<String>();
for (int i = 0; i < zinnia.zinnia_result_size(result); ++i) {
String value = zinnia.zinnia_result_value(result, i);
values.add(value);
}
return values.toArray(new String[0]);
} catch (Exception e) {
logger.log(Level.WARNING, "文字の認識に失敗しました", e);
} finally {
try {
if (recognizer != null) {
pool.returnObject(recognizer);
recognizer = null;
}
} catch (Exception e) {
e.printStackTrace();
}
if (result != null) {
zinnia.zinnia_result_destroy(result);
}
if (character != null) {
zinnia.zinnia_character_destroy(character);
}
}
return null;
}
@Override
public String getAvailableCharacters() {
try {
return availableCharacters.get(STATIC_KEY);
} catch (ExecutionException e) {
logger.log(Level.WARNING, "文字認識エンジンで使用可能な文字の取得に失敗しました");
return null;
}
}
}