/*
* Copyright (c) 2010-2016, Sikuli.org, sikulix.com
* Released under the MIT License.
*
*/
package org.sikuli.script;
import org.sikuli.basics.Settings;
import org.sikuli.basics.Debug;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Iterator;
import org.sikuli.natives.FindInput;
import org.sikuli.natives.FindResult;
import org.sikuli.natives.FindResults;
import org.sikuli.natives.TARGET_TYPE;
import org.sikuli.natives.Vision;
/**
* implements the process to find one image in another image <br>
* this is the historical implementation
* based on the C++ JNI access to the native OpenCV libraries<br>
* It is being replaced by ImageFinder, that implements the Finder features
* completely in Java using the OpenCV newly provided JAVA interface<br>
* At time of realisation the Finder API will be redirected to ImageFinder
*/
public class Finder implements Iterator<Match> {
static RunTime runTime = RunTime.get();
private Region _region = null;
private Pattern _pattern = null;
private Image _image = null;
private FindInput _findInput = new FindInput();
private FindResults _results = null;
private int _cur_result_i;
private boolean repeating = false;
private boolean valid = true;
private boolean screenFinder = true;
static {
RunTime.loadLibrary("VisionProxy");
}
private static String me = "Finder: ";
private static int lvl = 3;
private static void log(int level, String message, Object... args) {
Debug.logx(level, me + message, args);
}
//<editor-fold defaultstate="collapsed" desc="Constructors">
/**
* Just to force library initialization
*/
public Finder() {}
/**
* Finder constructor (finding within an image).
* <br>internally used with a screen snapshot
*
* @param imageFilename a string (name, path, url)
* @throws java.io.IOException if imagefile not found
*/
public Finder(String imageFilename) throws IOException {
this(imageFilename, null);
}
/**
* Finder constructor (finding within an image within the given region).
* <br>internally used with a screen snapshot
*
* @param imageFilename a string (name, path, url)
* @param region search Region within image - topleft = (0,0)
* @throws java.io.IOException if imagefile not found
*/
public Finder(String imageFilename, Region region) throws IOException {
Image img = Image.create(imageFilename);
if (img.isValid()) {
_findInput.setSource(Image.convertBufferedImageToMat(img.get()));
_region = region;
screenFinder = false;
} else {
log(-1, "imagefile not found:\n%s");
valid = false;
}
}
/**
* Constructor for special use from a BufferedImage
*
* @param bimg BufferedImage
*/
public Finder(BufferedImage bimg) {
_findInput.setSource(Image.convertBufferedImageToMat(bimg));
}
/**
* Finder constructor for special use from a ScreenImage
*
* @param simg ScreenImage
*/
public Finder(ScreenImage simg) {
initScreenFinder(simg, null);
}
/**
* Finder constructor for special use from a ScreenImage
*
* @param simg ScreenImage
* @param region the cropping region
*/
public Finder(ScreenImage simg, Region region) {
initScreenFinder(simg, region);
}
/**
* Finder constructor for special use from an Image
*
* @param img Image
*/
public Finder(Image img) {
log(lvl, "Image: %s", img);
_findInput.setSource(Image.convertBufferedImageToMat(img.get()));
}
public void resetImage(Image img) {
_findInput.setSource(Image.convertBufferedImageToMat(img.get()));
}
private void initScreenFinder(ScreenImage simg, Region region) {
setScreenImage(simg);
_region = region;
}
/**
* to explicitly free the Finder's resources
*/
public void destroy() {
_findInput.delete();
_findInput = null;
_results.delete();
_results = null;
_pattern = null;
}
/**
* not used
*/
@Override
public void remove(){}
@Override
protected void finalize() throws Throwable {
super.finalize();
destroy();
}
/**
* internal use: exchange the source image in existing Finder
*
* @param simg ScreenImage
*/
protected void setScreenImage(ScreenImage simg) {
_findInput.setSource(Image.convertBufferedImageToMat(simg.getImage()));
}
public boolean isValid() {
return valid;
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="internal repeating">
/**
* internal use: to be able to reuse the same Finder
*/
protected void setRepeating() {
repeating = true;
}
/**
* internal use: repeat with same Finder
*/
protected void findRepeat() {
_results = Vision.find(_findInput);
_cur_result_i = 0;
}
/**
* internal use: repeat with same Finder
*/
protected void findAllRepeat() {
Debug timing = Debug.startTimer("Finder.findAll");
_results = Vision.find(_findInput);
_cur_result_i = 0;
timing.end();
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="find">
/**
* do a find op with the given image or the given text in the Finder's image
* (hasNext() and next() will reveal possible match results)
* @param imageOrText image file name or text
* @return null. if find setup not possible
*/
public String find(String imageOrText) {
if (!valid) {
log(-1, "not valid");
return null;
}
Image img = Image.create(imageOrText);
if (img.isText()) {
return findText(imageOrText);
}
if (img.isValid()) {
return find(img);
}
return null;
}
/**
* do a find op with the given pattern in the Finder's image
* (hasNext() and next() will reveal possible match results)
* @param aPtn Pattern
* @return null. if find setup not possible
*/
public String find(Pattern aPtn) {
if (!valid) {
log(-1, "not valid");
return null;
}
if (aPtn.isValid()) {
_pattern = aPtn;
_findInput.setTarget(aPtn.getImage().getMatNative());
_findInput.setSimilarity(aPtn.getSimilar());
_results = Vision.find(_findInput);
_cur_result_i = 0;
return aPtn.getFilename();
} else {
return null;
}
}
/**
* do a find op with the given pattern in the Finder's image
* (hasNext() and next() will reveal possible match results)
* @param img Image
* @return null. if find setup not possible
*/
public String find(Image img) {
if (!valid) {
log(-1, "not valid");
return null;
}
if (img.isValid()) {
_findInput.setTarget(img.getMatNative());
_findInput.setSimilarity(Settings.MinSimilarity);
_results = Vision.find(_findInput);
_cur_result_i = 0;
return img.getFilename();
} else if (img.isUseable()) {
return find(new Pattern(img));
} else {
return null;
}
}
/**
* do a text find with the given text in the Finder's image
* (hasNext() and next() will reveal possible match results)
* @param text text
* @return null. if find setup not possible
*/
public String findText(String text) {
if (!valid) {
log(-1, "not valid");
return null;
}
_findInput.setTarget(TARGET_TYPE.TEXT, text);
_results = Vision.find(_findInput);
_cur_result_i = 0;
return text;
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="findAll">
/**
* do a findAll op with the given image or the given text in the Finder's image
* (hasNext() and next() will reveal possible match results)
* @param imageOrText iamge file name or text
* @return null. if find setup not possible
*/
public String findAll(String imageOrText) {
if (!valid) {
log(-1, "not valid");
return null;
}
Image img = Image.create(imageOrText);
_image = img;
if (img.isText()) {
return findAllText(imageOrText);
}
if (img.isValid()) {
return findAll(img);
}
return null;
}
/**
* do a find op with the given pattern in the Finder's image
* (hasNext() and next() will reveal possible match results)
* @param aPtn Pattern
* @return null. if find setup not possible
*/
public String findAll(Pattern aPtn) {
if (!valid) {
log(-1, "not valid");
return null;
}
if (aPtn.isValid()) {
_image = aPtn.getImage();
_pattern = aPtn;
_findInput.setTarget(aPtn.getImage().getMatNative());
_findInput.setSimilarity(aPtn.getSimilar());
_findInput.setFindAll(true);
Debug timing = Debug.startTimer("Finder.findAll");
_results = Vision.find(_findInput);
_cur_result_i = 0;
timing.end();
return aPtn.getFilename();
} else {
return null;
}
}
/**
* do a findAll op with the given image in the Finder's image
* (hasNext() and next() will reveal possible match results)
* @param img Image
* @return null. if find setup not possible
*/
public String findAll(Image img) {
if (!valid) {
log(-1, "not valid");
return null;
}
if (img.isValid()) {
_findInput.setTarget(img.getMatNative());
_findInput.setSimilarity(Settings.MinSimilarity);
_findInput.setFindAll(true);
Debug timing = Debug.startTimer("Finder.findAll");
_results = Vision.find(_findInput);
_cur_result_i = 0;
timing.end();
return img.getFilename();
} else {
return null;
}
}
/**
* do a findAll op with the given text in the Finder's image
* (hasNext() and next() will reveal possible match results)
* @param text text
* @return null. if find setup not possible
*/
public String findAllText(String text) {
if (!valid) {
log(-1, "not valid");
return null;
}
_findInput.setTarget(TARGET_TYPE.TEXT, text);
_findInput.setFindAll(true);
Debug timing = Debug.startTimer("Finder.findAllText");
_results = Vision.find(_findInput);
_cur_result_i = 0;
timing.end();
return text;
}
//</editor-fold>
private String setTargetSmartly(FindInput fin, String target) {
if (isImageFile(target)) {
//assume it's a file first
String filename = Image.create(target).getFilename();
if (filename != null) {
fin.setTarget(TARGET_TYPE.IMAGE, filename);
return filename;
} else {
if (!repeating) {
Debug.error(target
+ " looks like a file, but not on disk. Assume it's text.");
}
}
}
if (!Settings.OcrTextSearch) {
Debug.error("Region.find(text): text search is currently switched off");
return target + "???";
} else {
fin.setTarget(TARGET_TYPE.TEXT, target);
if (TextRecognizer.getInstance() == null) {
Debug.error("Region.find(text): text search is now switched off");
return target + "???";
}
return target;
}
}
private static boolean isImageFile(String fname) {
int dot = fname.lastIndexOf('.');
if (dot < 0) {
return false;
}
String suffix = fname.substring(dot + 1).toLowerCase();
if (suffix.equals("png") || suffix.equals("jpg")) {
return true;
}
return false;
}
/**
*
* @return true if Finder has a next match, false otherwise
*/
@Override
public boolean hasNext() {
if (_results != null && _results.size() > _cur_result_i) {
return true;
}
return false;
}
/**
*
* @return the next match or null
*/
@Override
public Match next() {
Match match = null;
if (hasNext()) {
FindResult fr = _results.get(_cur_result_i++);
IScreen parentScreen = null;
if (screenFinder && _region != null) {
parentScreen = _region.getScreen();
}
match = new Match(fr, parentScreen);
match.setOnScreen(screenFinder);
fr.delete();
if (_region != null) {
match = _region.toGlobalCoord(match);
}
if (_pattern != null) {
Location offset = _pattern.getTargetOffset();
match.setTargetOffset(offset);
}
match.setImage(_image);
}
return match;
}
}