org.sikuli.script.Image Maven / Gradle / Ivy
Show all versions of sikulixapi Show documentation
/*
* Copyright (c) 2010-2019, sikuli.org, sikulix.com - MIT license
*/
package org.sikuli.script;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import org.apache.commons.io.FilenameUtils;
import org.sikuli.basics.Debug;
import org.sikuli.basics.FileManager;
import org.sikuli.basics.Settings;
/**
* This class hides the complexity behind image names given as string.
*
Image does not have public nor public constructors: use create()
*
It's companion is {@link ImagePath} that maintains a list of places, where image files are
* loaded from.
* An Image object:
* - has a name, either given or taken from the basename
* - keeps it's in memory buffered image in a configurable cache avoiding reload
* from source
* - remembers, where it was found when searched the last time
* - can be sourced from the filesystem, from jars, from the web and from other
* in memory images
* - will have features for basic image manipulation and presentation
* - contains the stuff to communicate with the underlying OpenCV based search
* engine
*
* This class maintains
* - a list of all images ever loaded in this session with their source
* reference and a ref to the image object
* - a list of all images currently having their content in memory (buffered
* image) (managed as a configurable cache)
* The caching can be configured using {@link Settings#setImageCache(int)}
*/
public class Image {
private static String me = "Image: ";
private static int lvl = 3;
private static void log(int level, String message, Object... args) {
Debug.logx(level, me + message, args);
}
private static List images = Collections.synchronizedList(new ArrayList());
private static Map imageFiles = Collections.synchronizedMap(new HashMap());
private static Map imageNames = Collections.synchronizedMap(new HashMap());
//
public static Image getDefaultInstance4py() {
return new Image(new Screen().capture());
}
private Image() {
}
private Image(String fname, URL fURL) {
init(fname, fURL);
}
private Image(URL fURL) {
if ("file".equals(fURL.getProtocol())) {
init(fURL.getPath(), fURL);
} else {
init(getNameFromURL(fURL), fURL);
}
}
private void init(String fileName, URL fURL) {
imageName = fileName;
if (imageName.isEmpty() || fURL == null) {
return;
}
fileURL = fURL;
if (ImagePath.isImageBundled(fURL)) {
imageIsBundled = true;
imageName = new File(imageName).getName();
}
load();
}
public static void reinit(Image img) {
URL fURL = null;
File imgFile = new File(img.getName());
if (imgFile.isAbsolute()) {
if (imgFile.exists()) {
fURL = FileManager.makeURL(img.getName());
}
} else {
fURL = imageNames.get(img.getName());
if (fURL == null) {
fURL = ImagePath.find(img.getName());
}
}
if (fURL != null) {
img.init(img.getName(), fURL);
}
}
private Image copy() {
Image imgTarget = new Image();
imgTarget.setName(imageName);
imgTarget.setFileURL(fileURL);
imgTarget.setBimg(bimg);
imgTarget.setIsAbsolute(imageIsAbsolute);
imgTarget.setIsText(imageIsText);
imgTarget.setIsBundled(imageIsBundled);
imgTarget.setLastSeen(getLastSeen(), getLastSeenScore());
imgTarget.setHasIOException(hasIOException());
if (isPattern()) {
imgTarget.setSimilarity(similarity);
imgTarget.setOffset(offset);
imgTarget.setWaitAfter(waitAfter);
imgTarget.setIsPattern(true);
}
return imgTarget;
}
/**
* create a new image from a buffered image
* can only be reused with the object reference
*
* @param img BufferedImage
*/
public Image(BufferedImage img) {
this(img, null);
}
/**
* create a new image from a buffered image
* giving it a descriptive name for printout and logging
* can only be reused with the object reference
*
* @param img BufferedImage
* @param name descriptive name
*/
public Image(BufferedImage img, String name) {
imageName = isBImg;
if (name != null) {
imageName += name;
}
bimg = img;
bwidth = bimg.getWidth();
bheight = bimg.getHeight();
log(lvl, "BufferedImage: (%d, %d)%s", bwidth, bheight,
(name == null ? "" : " with name: " + name));
}
/**
* create a new image from a Sikuli ScreenImage (captured)
* can only be reused with the object reference
*
* @param img ScreenImage
*/
public Image(ScreenImage img) {
this(img.getImage(), null);
}
/**
* create a new image from a Sikuli ScreenImage (captured)
* giving it a descriptive name for printout and logging
* can only be reused with the object reference
*
* @param img ScreenImage
* @param name descriptive name
*/
public Image(ScreenImage img, String name) {
this(img.getImage(), name);
}
/**
* check whether image is available for Finder.find()
* This is for backward compatibility
* The new ImageFinder uses isUsable()
*
* @return true if lodable from file or is an in memory image
*/
public boolean isValid() {
return fileURL != null || getName().contains(isBImg);
}
/**
* checks, wether the Image can be used with the new ImageFinder
*
* @return true/false
*/
public boolean isUseable() {
return isValid() || imageIsPattern;
}
@Override
public String toString() {
return String.format(
(imageName != null ? imageName : "__UNKNOWN__") + ": (%dx%d)", bwidth, bheight)
+ (lastSeen == null ? ""
: String.format(" seen at (%d, %d) with %.2f", lastSeen.x, lastSeen.y, lastScore));
}
//
//
/**
* @return the image's absolute filename or null if jar, http or in memory
* image
*/
public String getFilename() {
if (fileURL != null && "file".equals(fileURL.getProtocol())) {
return new File(fileURL.getPath()).getAbsolutePath();
} else {
return imageName;
}
}
private boolean bHasIOException = false;
public boolean hasIOException() {
return bHasIOException;
}
public void setHasIOException(boolean state) {
bHasIOException = state;
}
/**
* Get the image's descriptive name
*
* @return the name
*/
public String getName() {
if (isText()) {
return imageNameGiven;
}
return imageName;
}
public Image setName(String imageName) {
this.imageName = imageName;
return this;
}
private String imageName = null;
private String imageNameGiven = null;
//
//
/**
* @return the evaluated url for this image (might be null)
*/
public URL getURL() {
return fileURL;
}
public Image setFileURL(URL fileURL) {
this.fileURL = fileURL;
return this;
}
private URL fileURL = null;
private static Image get(URL imgURL) {
return imageFiles.get(imgURL);
}
private static String getNameFromURL(URL fURL) {
//TODO add handling for http
if ("jar".equals(fURL.getProtocol())) {
int n = fURL.getPath().lastIndexOf(".jar!/");
int k = fURL.getPath().substring(0, n).lastIndexOf("/");
if (n > -1) {
return "JAR:" + fURL.getPath().substring(k + 1, n) + fURL.getPath().substring(n + 5);
}
}
return "???:" + fURL.getPath();
}
public boolean isFile() {
if (isValid()) {
URL furl = getURL();
if ("file".equals(furl.getProtocol())) {
return true;
}
}
return false;
}
private boolean imageIsAbsolute = false;
/**
* @return true if image was given with absolute filepath
*/
public boolean isAbsolute() {
return imageIsAbsolute;
}
public Image setIsAbsolute(boolean val) {
imageIsAbsolute = val;
return this;
}
//
//
public Image setBimg(BufferedImage bimg) {
this.bimg = bimg;
if (bimg != null) {
bwidth = bimg.getWidth();
bheight = bimg.getHeight();
bsize = bimg.getData().getDataBuffer().getSize();
} else {
bsize = 0;
bwidth = -1;
bheight = -1;
}
return this;
}
private BufferedImage bimg = null;
private int bsize = 0;
private int bwidth = -1;
private int bheight = -1;
public static BufferedImage getSubimage(BufferedImage bimg, Rectangle rect) {
return bimg.getSubimage(rect.x, rect.y, (int) rect.getWidth(), (int) rect.getHeight());
}
/**
* return the image's BufferedImage (load it if not in cache)
*
* @return BufferedImage (might be null)
*/
public BufferedImage get() {
if (bimg != null) {
if (fileURL == null) {
log(lvl + 1, "getImage inMemory: %s", imageName);
} else {
log(lvl + 1, "getImage from cache: %s", imageName);
}
return bimg;
} else {
return load();
}
}
/**
* @return size of image
*/
public Dimension getSize() {
return new Dimension(bwidth, bheight);
}
private int getKB() {
if (bimg == null) {
return 0;
}
return (int) bsize / KB;
}
/**
* resize the loaded image with factor using Graphics2D.drawImage
*
* @param factor resize factor
* @return a new BufferedImage resized (width*factor, height*factor)
*/
public BufferedImage resize(float factor) {
int type;
return resize(get(), factor);
}
public static BufferedImage resize(BufferedImage bimg, float factor) {
int type = bimg.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : bimg.getType();
int width = (int) (bimg.getWidth() * factor);
int height = (int) (bimg.getHeight() * factor);
BufferedImage resizedImage = new BufferedImage(width, height, type);
Graphics2D g = resizedImage.createGraphics();
g.drawImage(bimg, 0, 0, width, height, null);
g.dispose();
return resizedImage;
}
/**
* create a sub image from this image
*
* @param x pixel column
* @param y pixel row
* @param w width
* @param h height
* @return the new image
*/
public Image getSub(int x, int y, int w, int h) {
BufferedImage bi = createBufferedImage(w, h);
Graphics2D g = bi.createGraphics();
g.drawImage(get().getSubimage(x, y, w, h), 0, 0, null);
g.dispose();
return new Image(bi);
}
private static BufferedImage createBufferedImage(int w, int h) {
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
int[] nBits = {8, 8, 8, 8};
ColorModel cm = new ComponentColorModel(cs, nBits, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
SampleModel sm = cm.createCompatibleSampleModel(w, h);
DataBufferByte db = new DataBufferByte(w * h * 4);
WritableRaster r = WritableRaster.createWritableRaster(sm, db, new Point(0, 0));
BufferedImage bm = new BufferedImage(cm, r, false, null);
return bm;
}
//
//
private boolean imageIsText = false;
/**
* @return true if the given image name did not give a valid image so it might
* be text to search
*/
public boolean isText() {
return imageIsText;
}
public Image setIsText(boolean val) {
imageIsText = val;
return this;
}
public String getNameAsText() {
return imageNameGiven;
}
//
//
private boolean imageIsBundled = false;
private Image setIsBundled(boolean imageIsBundled) {
this.imageIsBundled = imageIsBundled;
return this;
}
/**
* INTERNAL USE: image is contained in a bundle (.sikuli)
*
* @return true/false
*/
public boolean isBundled() {
return imageIsBundled;
}
//
//
private boolean imageIsPattern = false;
/**
* true if this image contains pattern aspects
* only useable with the new ImageFinder
*
* @return true if yes, false otherwise
*/
public boolean isPattern() {
return imageIsPattern;
}
public Image setIsPattern(boolean imageIsPattern) {
this.imageIsPattern = imageIsPattern;
return this;
}
private Location offset = new Location(0, 0);
/**
* Get the value of offset
*
* @return the value of offset
*/
public Location getOffset() {
return offset;
}
/**
* Set the value of offset
*
* @param offset new value of offset
* @return the image
*/
public Image setOffset(Location offset) {
this.offset = offset;
return this;
}
private double similarity = Settings.MinSimilarity;
/**
* Get the value of similarity
*
* @return the value of similarity
*/
public double getSimilarity() {
return similarity;
}
/**
* Set the value of similarity
*
* @param similarity new value of similarity
* @return the image
*/
public Image setSimilarity(double similarity) {
this.similarity = similarity;
return this;
}
//
//
/**
* create a sub image from this image
*
* @param part (the constants Region.XXX as used with {@link Region#get(int)})
* @return the sub image
*/
public Image getSub(int part) {
Rectangle r = Region.getRectangle(new Rectangle(0, 0, getSize().width, getSize().height), part);
return getSub(r.x, r.y, r.width, r.height);
}
/**
* create a new Image as copy of the given Image
*
* @param imgSrc given Image
* @return new Image
*/
public static Image create(Image imgSrc) {
return imgSrc.copy();
}
/**
* create a new image from a filename
* file ending .png is added if missing (currently valid: png, jpg, jpeg)
* relative filename: [...path.../]name[.png] is searched on current image path
* absolute filename is taken as is
* if image exists, it is loaded to cache
* already loaded image with same name (given path) is reused (taken from cache)
*
* if image not found, it might be a text to be searched (imageIsText = true)
*
* @param fName image filename
* @return an Image object (might not be valid - check with isValid())
*/
public static Image create(String fName) {
Image img = get(fName);
return createImageValidate(img);
}
/**
* create a new Image with Pattern aspects from an existing Pattern
*
* @param p a Pattern
* @return the new Image
*/
public static Image create(Pattern p) {
Image img = p.getImage().copy();
img.setIsPattern(true);
img.setSimilarity(p.getSimilar());
img.setOffset(p.getTargetOffset());
img.setWaitAfter(p.getTimeAfter());
return img;
}
/**
* create a new image from the given url
* file ending .png is added if missing
* filename: ...url-path.../name[.png] is loaded from the url and and cached
*
* already loaded image with same url is reused (reference) and taken from
* cache
*
* @param url image file URL
* @return the image
*/
public static Image create(URL url) {
Image img = get(url);
if (img == null) {
img = new Image(url);
}
return createImageValidate(img);
}
public static Image getImageFromTarget(PSI target) {
if (target instanceof Pattern) {
return ((Pattern) target).getImage();
} else if (target instanceof String) {
Image img = get((String) target);
img = createImageValidate(img);
return img;
} else if (target instanceof Image) {
return (Image) target;
} else if (target instanceof ScreenImage) {
return new Image(((ScreenImage) target).getImage());
} else {
throw new RuntimeException(String.format("SikuliX: find, wait, exists: invalid parameter: %s", target));
}
}
/**
* FOR INTERNAL USE: from IDE - suppresses load error message
*
* @param fName image filename
* @return this
*/
public static Image createThumbNail(String fName) {
Image img = get(fName);
return createImageValidate(img);
}
private static Image createImageValidate(Image img) {
if (img == null) {
return new Image("", null);
}
if (!img.isValid()) {
if (Settings.OcrTextSearch || Settings.SwitchToText) {
if (isValidImageFilename(img.getName())) {
img.setIsText(false);
} else {
img.setIsText(true);
}
} else {
log(-1, "Image not valid, but TextSearch is switched off!");
}
}
return img;
}
public static boolean isValidImageFilename(String fname) {
String validEndings = ".png.jpg.jpeg";
String ending = FilenameUtils.getExtension(fname);
return !ending.isEmpty() && validEndings.contains(ending.toLowerCase());
}
public static String getValidImageFilename(String fname) {
if (isValidImageFilename(fname)) {
return fname;
}
return fname + ".png";
}
//
//
private static final int KB = 1024;
private static final int MB = KB * KB;
private final static String isBImg = "__BufferedImage__";
private static long currentMemory = 0;
private static synchronized long currentMemoryChange(long size, long max) {
long maxMemory = max;
if (max < 0) {
maxMemory = Settings.getImageCache() * MB;
currentMemory += size;
}
if (currentMemory > maxMemory) {
Image first;
while (images.size() > 0 && currentMemory > maxMemory) {
first = images.remove(0);
first.bimg = null;
currentMemory -= first.bsize;
}
if (maxMemory == 0) {
currentMemory = 0;
} else {
currentMemory = Math.max(0, currentMemory);
}
}
if (size < 0) {
currentMemory = Math.max(0, currentMemory);
}
return currentMemory;
}
private static long currentMemoryUp(long size) {
return currentMemoryChange(size, -1);
}
private static long currentMemoryDown(long size) {
currentMemory -= size;
currentMemory = Math.max(0, currentMemory);
return currentMemoryChange(-size, -1);
}
private static long currentMemoryDownUp(int sizeOld, int sizeNew) {
currentMemoryDown(sizeOld);
return currentMemoryUp(sizeNew);
}
private static boolean isCaching() {
return Settings.getImageCache() > 0;
}
public static void clearCache(int maxSize) {
currentMemoryChange(0, maxSize);
}
public static void purge() {
purge(ImagePath.getBundle());
}
public static void purge(ImagePath.PathEntry path) {
if (path == null) {
return;
}
purge(path.pathURL);
}
private static synchronized void purge(URL pathURL) {
List imagePurgeList = new ArrayList<>();
List imageNamePurgeList = new ArrayList<>();
URL imgURL;
Image img;
log(lvl + 1, "purge: ImagePath: %s", pathURL.getPath());
Iterator> it = imageFiles.entrySet().iterator();
Map.Entry entry;
while (it.hasNext()) {
entry = it.next();
imgURL = entry.getKey();
if (imgURL.toString().startsWith(pathURL.toString())) {
log(lvl + 1, "purge: URL: %s", imgURL.toString());
img = entry.getValue();
imagePurgeList.add(img);
imageNamePurgeList.add(img.imageName);
it.remove();
}
}
if (!imagePurgeList.isEmpty()) {
Iterator bit = images.iterator();
while (bit.hasNext()) {
img = bit.next();
if (imagePurgeList.contains(img)) {
bit.remove();
log(lvl + 1, "purge: bimg: %s", img);
currentMemoryDown(img.bsize);
}
}
}
for (String name : imageNamePurgeList) {
imageNames.remove(name);
}
}
private static void unCache(URL imgURL) {
Image img = imageFiles.get(imgURL);
if (img == null) {
return;
}
currentMemoryDown(img.bsize);
img.setBimg(null);
images.remove(img);
}
//TODO make obsolete
public static void unCache(String fileName) {
unCache(FileManager.makeURL(new File(fileName).getAbsolutePath()));
}
/**
* Print the current state of the cache
*/
public static void dump() {
dump(0);
}
/**
* Print the current state of the cache, verbosity depends on debug level
*
* @param lvl debug level used here
*/
public static void dump(int lvl) {
log(lvl, "--- start of Image dump ---");
ImagePath.dump(lvl);
log(lvl, "ImageFiles entries: %d", imageFiles.size());
Iterator> it = imageFiles.entrySet().iterator();
Map.Entry entry;
while (it.hasNext()) {
entry = it.next();
log(lvl, entry.getKey().toString());
}
log(lvl, "ImageNames entries: %d", imageNames.size());
Iterator> nit = imageNames.entrySet().iterator();
Map.Entry name;
while (nit.hasNext()) {
name = nit.next();
log(lvl, "%s %d KB (%s)", new File(name.getKey()).getName(),
imageFiles.get(name.getValue()).getKB(), name.getValue());
}
if (Settings.getImageCache() == 0) {
log(lvl, "Cache state: switched off!");
} else {
log(lvl, "Cache state: Max %d MB (entries: %d used: %d %% %d KB)",
Settings.getImageCache(), images.size(),
(int) (100 * currentMemory / (Settings.getImageCache() * MB)), (int) (currentMemory / KB));
}
log(lvl, "--- end of Image dump ---");
}
/**
* clears all caches (should only be needed for debugging)
*/
public static void reset() {
clearCache(0);
imageNames.clear();
imageFiles.clear();
}
public File remove() {
URL furl = null;
if (isFile()) {
furl = getURL();
unCache(furl);
return new File(furl.getPath());
}
return null;
}
public void delete() {
File fImg = remove();
if (null != fImg) FileManager.deleteFileOrFolder(fImg);
}
private String hasBackup = "";
public boolean backup() {
if (isValid()) {
File fOrg = new File(fileURL.getPath());
File fBack = new File(fOrg.getParentFile(), "_BACKUP_" + fOrg.getName());
if (FileManager.xcopy(fOrg, fBack)) {
hasBackup = fBack.getPath();
log(lvl, "backup: %s created", fBack.getName());
return true;
}
log(-1, "backup: %s did not work", fBack.getName());
}
return false;
}
public boolean restore() {
if (!hasBackup.isEmpty()) {
File fBack = new File(hasBackup);
File fOrg = new File(hasBackup.replace("_BACKUP_", ""));
if (FileManager.xcopy(fBack, fOrg)) {
log(lvl, "restore: %s restored", fOrg.getName());
FileManager.deleteFileOrFolder(fBack);
hasBackup = "";
return true;
}
log(-1, "restore: %s did not work", fBack.getName());
}
return false;
}
//
//
/**
* FOR INTERNAL USE: tries to get the image from the cache, if not cached yet:
* create and load a new image
*
* @param fName image filename
* @return this
*/
private static Image get(String fName) {
if (fName == null || fName.isEmpty()) {
return null;
}
Image image = null;
if (fName.startsWith("\t") && fName.endsWith("\t")) {
fName = fName.substring(1, fName.length() - 1);
image = new Image();
image.setIsText(true);
} else {
URL imageURL = null;
String imageFileName = getValidImageFilename(fName);
if (imageFileName.isEmpty()) {
log(-1, "not a valid image type: " + fName);
imageFileName = fName;
}
File imageFile = new File(imageFileName);
if (imageFile.isAbsolute() && imageFile.exists()) {
try {
imageURL = new URL("file", null, imageFile.getPath());
} catch (MalformedURLException e) {
}
} else {
imageURL = imageNames.get(imageFileName);
if (imageURL == null) {
imageURL = ImagePath.find(imageFileName);
}
}
if (imageURL != null) {
image = imageFiles.get(imageURL);
if (image != null && null == imageNames.get(image.imageName)) {
imageNames.put(image.imageName, imageURL);
}
}
if (image == null) {
image = new Image(imageFileName, imageURL);
image.setIsAbsolute(imageFile.isAbsolute());
} else {
if (image.bimg != null) {
log(3, "reused: %s (%s)", image.imageName, image.fileURL);
} else {
if (Settings.getImageCache() > 0) {
image.load();
}
}
}
}
image.imageNameGiven = fName;
return image;
}
private BufferedImage load() {
BufferedImage bImage = null;
if (fileURL != null) {
bimg = null;
try {
bImage = ImageIO.read(fileURL);
} catch (Exception e) {
log(-1, "load: failed: %s", fileURL);
bHasIOException = true;
fileURL = null;
return null;
}
if (imageName != null) {
imageFiles.put(fileURL, this);
imageNames.put(imageName, fileURL);
bwidth = bImage.getWidth();
bheight = bImage.getHeight();
bsize = bImage.getData().getDataBuffer().getSize();
log(lvl, "loaded: %s (%s)", imageName, fileURL);
if (isCaching()) {
int maxMemory = Settings.getImageCache() * MB;
currentMemoryUp(bsize);
bimg = bImage;
images.add(this);
log(lvl, "cached: %s (%d KB) (# %d KB %d -- %d %% of %d MB)",
imageName, getKB(),
images.size(), (int) (currentMemory / KB),
(int) (100 * currentMemory / maxMemory), (int) (maxMemory / MB));
}
} else {
log(-1, "invalid! not loaded! %s", fileURL);
}
}
return bImage;
}
private BufferedImage loadAgain() {
BufferedImage bImage = null;
if (fileURL != null) {
bimg = null;
try {
bImage = ImageIO.read(fileURL);
} catch (Exception e) {
log(-1, "loadAgain: failed: %s", fileURL);
bHasIOException = true;
imageFiles.remove(fileURL);
return null;
}
imageFiles.put(fileURL, this);
imageNames.put(imageName, fileURL);
bwidth = bImage.getWidth();
bheight = bImage.getHeight();
bsize = bImage.getData().getDataBuffer().getSize();
log(lvl, "loaded again: %s (%s)", imageName, fileURL);
}
return bImage;
}
public static void reload(String fpImage) {
// URL uImage = FileManager.makeURL(fpImage);
URL uImage = imageNames.get(fpImage);
if (imageFiles.containsKey(uImage)) {
Image image = imageFiles.get(uImage);
int sizeOld = image.bsize;
if (null != image.loadAgain()) {
currentMemoryDownUp(sizeOld, image.bsize);
image.setLastSeen(null, 0);
}
}
}
public static void setIDEshouldReload(Image img) {
ideShouldReload = true;
img.wasRecaptured = true;
img.lastSeen = null;
}
public static boolean getIDEshouldReload() {
boolean state = ideShouldReload;
ideShouldReload = false;
return state;
}
private static boolean ideShouldReload = false;
public boolean isRecaptured() {
boolean state = wasRecaptured;
wasRecaptured = false;
return state;
}
public boolean wasRecaptured = false;
//
//
private int waitAfter;
/**
* Get the value of waitAfter
*
* @return the value of waitAfter
*/
public int getWaitAfter() {
return waitAfter;
}
/**
* Set the value of waitAfter
*
* @param waitAfter new value of waitAfter
* @return the image
*/
public Image setWaitAfter(int waitAfter) {
this.waitAfter = waitAfter;
return this;
}
//
//
private Rectangle lastSeen = null;
private double lastScore = 0.0;
/**
* if the image was already found before
*
* @return the rectangle where it was found
*/
public Rectangle getLastSeen() {
return lastSeen;
}
/**
* if the image was already found before
*
* @return the similarity score
*/
public double getLastSeenScore() {
return lastScore;
}
/**
* Internal Use: set the last seen info after a find
*
* @param lastSeen Match
* @param sim SimilarityScore
* @return the image
*/
public Image setLastSeen(Rectangle lastSeen, double sim) {
this.lastSeen = lastSeen;
this.lastScore = sim;
return this;
}
//
//
/**
* to support a raster over the image
*/
private int rows = 0;
private int cols = 0;
private int rowH = 0;
private int colW = 0;
private int rowHd = 0;
private int colWd = 0;
/**
* store info: this image is divided vertically into n even rows
* a preparation for using getRow()
*
* @param n number of rows
* @return the top row
*/
public Image setRows(int n) {
return setRaster(n, 0);
}
/**
* store info: this image is divided horizontally into n even columns
* a preparation for using getCol()
*
* @param n number of Columns
* @return the leftmost column
*/
public Image setCols(int n) {
return setRaster(0, n);
}
/**
* @return number of eventually defined rows in this image or 0
*/
public int getRows() {
return rows;
}
/**
* @return height of eventually defined rows in this image or 0
*/
public int getRowH() {
return rowH;
}
/**
* @return number of eventually defined columns in this image or 0
*/
public int getCols() {
return cols;
}
/**
* @return width of eventually defined columns in this image or 0
*/
public int getColW() {
return colW;
}
/**
* store info: this image is divided into a raster of even cells
* a preparation for using getCell()
*
* @param r number of rows
* @param c number of columns
* @return the top left cell
*/
public Image setRaster(int r, int c) {
rows = r;
cols = c;
if (r > 0) {
rowH = (int) (getSize().height / r);
rowHd = getSize().height - r * rowH;
}
if (c > 0) {
colW = (int) (getSize().width / c);
colWd = getSize().width - c * colW;
}
return getCell(0, 0);
}
/**
* get the specified row counting from 0, if rows or raster are setup
negative
* counts reverse from the end (last = -1)
values outside range are 0 or last
* respectively
*
* @param r row number
* @return the row as new image or the image itself, if no rows are setup
*/
public Image getRow(int r) {
if (rows == 0) {
return this;
}
if (r < 0) {
r = rows + r;
}
r = Math.max(0, r);
r = Math.min(r, rows - 1);
return getSub(0, r * rowH, getSize().width, rowH);
}
/**
* get the specified column counting from 0, if columns or raster are setup
* negative counts reverse from the end (last = -1)
values outside range are 0
* or last respectively
*
* @param c column number
* @return the column as new image or the image itself, if no columns are
* setup
*/
public Image getCol(int c) {
if (cols == 0) {
return this;
}
if (c < 0) {
c = cols + c;
}
c = Math.max(0, c);
c = Math.min(c, cols - 1);
return getSub(c * colW, 0, colW, getSize().height);
}
/**
* get the specified cell counting from (0, 0), if a raster is setup
* negative counts reverse from the end (last = -1)
values outside range are 0
* or last respectively
*
* @param r row number
* @param c column number
* @return the cell as new image or the image itself, if no raster is setup
*/
public Image getCell(int r, int c) {
if (rows == 0) {
return getCol(c);
}
if (cols == 0) {
return getRow(r);
}
if (rows == 0 && cols == 0) {
return this;
}
if (r < 0) {
r = rows - r;
}
if (c < 0) {
c = cols - c;
}
r = Math.max(0, r);
r = Math.min(r, rows - 1);
c = Math.max(0, c);
c = Math.min(c, cols - 1);
return getSub(c * colW, r * rowH, colW, rowH);
}
//
/**
* OCR-read the text from the image
*
* @return the text or empty string
*/
public String text() {
return TextRecognizer.doOCR(get());
}
/**
* convenience method: get text from given image file
*
* @param imgFile image filename
* @return the text or null
*/
public static String text(String imgFile) {
return create(imgFile).text();
}
}