This is an automated email from the git hooks/post-receive script. pini pushed a commit to tag upstream/1.1.0_beta1 in repository sikuli.
commit ff0a7c44d1fe3795835bee54e0eb0c6e9930b1b8 Author: Raimund Hocke <[email protected]> Date: Thu Jan 9 11:58:35 2014 +0100 Completely revised Image and ImagePath --- API/src/main/java/org/sikuli/script/Image.java | 491 ++++++++++++--------- API/src/main/java/org/sikuli/script/ImagePath.java | 58 ++- 2 files changed, 326 insertions(+), 223 deletions(-) diff --git a/API/src/main/java/org/sikuli/script/Image.java b/API/src/main/java/org/sikuli/script/Image.java index a485043..f21f50a 100644 --- a/API/src/main/java/org/sikuli/script/Image.java +++ b/API/src/main/java/org/sikuli/script/Image.java @@ -38,19 +38,26 @@ import org.sikuli.natives.Vision; /** * This class hides the complexity behind image names given as string.<br> - * Its companion is ImagePath that maintains a list of places, where images are stored.<br> - * Another companion is ImageGroup allowing to to look at images in a folder as a group.<br> + * Its companion is ImagePath that maintains a list of places, where images are + * stored.<br> + * Another companion is ImageGroup allowing to to look at images in a folder as + * a group.<br> * An Image object:<br> * - has a name, either given or taken from the basename without ending.<br> - * - keeps its in memory buffered image in a configurable cache avoiding reload from source<br> + * - keeps its in memory buffered image in a configurable cache avoiding reload + * from source<br> * - remembers, where it was found the last time searched<br> - * - can be sourced from the filesystem, from jars, from the web and from other in memory images <br> + * - can be sourced from the filesystem, from jars, from the web and from other + * in memory images <br> * - it will have features for basic image manipulation <br> - * - it contains the stuff to communicate with the underlying OpenCV based search engine <br> + * - it contains the stuff to communicate with the underlying OpenCV based + * search engine <br> * * This class maintains<br> - * - a list of all images loaded with there source reference and a ref to the image object<br> - * - a list of all images currently storing their in memory buffered image (managed as a cache)<br> + * - a list of all images loaded with there source reference and a ref to the + * image object<br> + * - a list of all images currently storing their in memory buffered image + * (managed as a cache)<br> * */ public class Image { @@ -60,7 +67,6 @@ public class Image { } private static String me = "Image"; - private static String mem = ""; private static int lvl = 3; private static void log(int level, String message, Object... args) { @@ -70,6 +76,7 @@ public class Image { private static List<Image> images = Collections.synchronizedList(new ArrayList<Image>()); private static List<Image> purgeList = Collections.synchronizedList(new ArrayList<Image>()); private static Map<URL, Image> imageFiles = Collections.synchronizedMap(new HashMap<URL, Image>()); + private static Map<String, URL> imageNames = Collections.synchronizedMap(new HashMap<String, URL>()); private static int KB = 1024; private static int MB = KB * KB; private static int maxMemory = 64 * MB; @@ -77,7 +84,7 @@ public class Image { private static String imageFromJar = "__FROM_JAR__"; private final static String isBImg = "__BufferedImage__"; - private String imageName = null;; + private String imageName = null; private boolean imageIsText = false; private boolean imageIsAbsolute = false; private String filepath = null; @@ -100,37 +107,173 @@ public class Image { private int rowHd = 0; private int colWd = 0; - - @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)); + + (lastSeen == null ? "" + : String.format(" seen at (%d, %d) with %.2f", lastSeen.x, lastSeen.y, lastScore)); + } + + private Image() { } /** * create a new image from a filename <br> * file ending .png is added if missing <br> - * filename: [...path.../]name[.png] is searched on current image path and loaded to cache <br> - * already loaded image with same name is reused (reference) and taken from cache - * @param imgName + * filename: [...path.../]name[.png] is searched on current image path and + * loaded to cache <br> + * already loaded image with same name is reused (reference) and taken from + * cache <br> + * if image not found, it might be a text to be searched (imageIsText = true) + * + * @param fN * @return the image */ - public static Image create(String imgName) { - Image img = get(imgName); + public static Image create(String fName) { + Image img = get(fName); + return createImageValidate(img); + } + + protected static Image get(String fname) { + if (fname == null || fname.isEmpty()) { + return null; + } + boolean absoluteFileName = false; + boolean existsFileName = true; + Image img = null; + URL fURL = null; + String fileName = getImageFilename(fname); + if (fileName == null) { + log(-1, "not a valid image type: " + fname); + fileName = fname; + } else { + fileName = FileManager.slashify(fileName, false); + File imgFile = new File(fileName); + String fn = fileName; + if (imgFile.isAbsolute()) { + if (imgFile.exists()) { + String bundlePath = ImagePath.getBundlePath(); + if (bundlePath != null && fileName.startsWith(bundlePath)) { + fileName = new File(fileName).getName(); + } else { + absoluteFileName = true; + } + fURL = FileManager.makeURL(fn); + imageNames.put(fileName, fURL); + } else { + existsFileName = false; + } + } + if (existsFileName) { + fURL = imageNames.get(fileName); + if (fURL == null) { + fURL = ImagePath.find(fileName); + } + if (fURL != null) { + img = imageFiles.get(fURL); + } + } + } if (img == null) { - img = new Image(imgName); + img = new Image(fileName, fURL); + img.setIsAbsolute(absoluteFileName); } - return createImageValidate(img); + return img; + } + + private static String getImageFilename(String fname) { + //TODO valid imagefile endings - where to store? + int dot = fname.lastIndexOf("."); + String ending; + if (dot > 0) { + ending = fname.substring(dot).toLowerCase(); + if (!ending.equals(".png") && !ending.equals(".jpg") && !ending.equals(".jepg")) { + return null; + } + } else { + fname += ".png"; + } + return fname; + } + + private Image(String fname, URL fURL) { + init(fname, fURL); + } + + private void init(String fileName, URL fURL) { + imageName = fileName; + if (imageName.isEmpty() || fURL == null) { + return; + } + fileURL = fURL; + if ("file".equals(fileURL.getProtocol())) { + filepath = fileURL.getPath(); + } else if ("jar".equals(fileURL.getProtocol())) { + filepath = imageFromJar; + } else { + //TODO support for http image urls + log(-1, "URL not supported: " + fileURL); + return; + } + loadImage(); + } + + private BufferedImage loadImage() { + if (filepath != null) { + try { + bimg = ImageIO.read(fileURL); + } catch (Exception e) { + log(-1, "FatalError: image could not be loaded from " + filepath); + return null; + } + if (imageName != null) { + imageFiles.put(fileURL, this); + imageNames.put(imageName, fileURL); + log(lvl, "added to image list: %s \nwith URL: %s", + imageName, fileURL); + bwidth = bimg.getWidth(); + bheight = bimg.getHeight(); + bsize = bimg.getData().getDataBuffer().getSize(); + currentMemory += bsize; + Image first; + while (images.size() > 0 && currentMemory > maxMemory) { + first = images.remove(0); + currentMemory -= first.bsize; + } + images.add(this); + log(lvl, "loaded %s (%d KB of %d MB (%d / %d %%) (%d))", imageName, (int) (bsize / KB), + (int) (maxMemory / MB), images.size(), (int) (100 * currentMemory / maxMemory), + (int) (currentMemory / KB)); + } else { + log(-1, "ImageName invalid! not cached!"); + } + } + return bimg; + } + + private static Image createImageValidate(Image img) { + if (img == null) { + log(-1, "Image not valid, creating empty Image"); + return new Image("", null); + } + if (!img.isValid()) { + if (Settings.OcrTextSearch) { + img.setIsText(true); + } else { + log(-1, "Image not valid, but TextSearch is switched off!"); + } + } + return img; } /** * create a new image from the given url <br> * file ending .png is added if missing <br> - * filename: ...url-path.../name[.png] is loaded from the url and and cached <br> - * already loaded image with same url is reused (reference) and taken from cache + * filename: ...url-path.../name[.png] is loaded from the url and and cached + * <br> + * already loaded image with same url is reused (reference) and taken from + * cache * * @return the image */ @@ -142,6 +285,30 @@ public class Image { return createImageValidate(img); } + protected static Image get(URL imgURL) { + return imageFiles.get(imgURL); + } + + private Image(URL fURL) { + if ("file".equals(fURL.getProtocol())) { + init(fURL.getPath(), fURL); + } else { + init(getNameFromURL(fURL), fURL); + } + } + + 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(); + } + /** * create a new image from a buffered image<br> * can only be reused with the object reference @@ -194,173 +361,23 @@ public class Image { this(img.getImage(), name); } - private static Image createImageValidate(Image img) { - if (!img.isValid()) { - if (Settings.OcrTextSearch) { - img.setIsText(true); - } else { - log(-1, "Image not valid, but TextSearch is switched off!"); - } - } - return img; - } - - protected static Image get(URL imgURL) { - return imageFiles.get(imgURL); - } - - protected static Image get(String fname) { - if (!fname.endsWith(".png")) { - fname += ".png"; - } - URL fURL = ImagePath.find(fname); - if (fURL != null) { - return imageFiles.get(fURL); - } else { - return null; - } - } - - private Image() { - } - - private Image(String fname) { - init(fname); - } - - private void init(String fname) { - imageName = fname; - if (!fname.endsWith(".png")) { - fname += ".png"; - } - fname = FileManager.slashify(fname, false); - if (new File(fname).isAbsolute()) { - if (new File(fname).exists()) { - String bundlePath = ImagePath.getBundlePath(); - if (bundlePath != null && fname.startsWith(bundlePath)) { - imageName = new File(fname).getName(); - } else { - imageIsAbsolute = true; - } - filepath = fname; - fileURL = FileManager.makeURL(fname); - } else { - log(-1, "FatalError: not locatable: " + fname); - } - } else { - for (ImagePath.PathEntry path : ImagePath.getPaths()) { - if (path == null) { - continue; - } - if ("jar".equals(path.pathURL.getProtocol())) { - fileURL = FileManager.getURLForContentFromURL(path.pathURL, fname); - if (fileURL != null) { - filepath = imageFromJar; - imageName = getNameFromURL(fileURL); - break; - } - } else if ("file".equals(path.pathURL.getProtocol())) { - fileURL = FileManager.makeURL(path.pathURL, fname); - if (new File(fileURL.getPath()).exists()) { - filepath = fileURL.getPath(); - break; - } - } - } - if (filepath == null) { - log(-1, "not found on image path: " + fname); - ImagePath.printPaths(); - } - } - loadImage(); - } - - private Image(URL fURL) { - if ("file".equals(fURL.getProtocol())) { - init(fURL.getPath()); - } else if ("jar".equals(fURL.getProtocol())) { - imageName = getNameFromURL(fURL); - fileURL = fURL; - filepath = imageFromJar; - loadImage(); - } else if (fURL.getProtocol().startsWith("http")) { - log(-1, "FatalError: Image from http(s) not supported: " + fURL); - - } else { - log(-1, "FatalError: ImageURL not supported: " + fURL); - } - fileURL = fURL; - } - - 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 null; - } - - private BufferedImage loadImage() { - if (filepath != null) { - try { - bimg = ImageIO.read(this.fileURL); - } catch (Exception e) { - log(-1, "FatalError: image could not be loaded from " + filepath); - return null; - } - if (imageName != null) { - imageFiles.put(fileURL, this); - log(lvl, "added to image list: %s \nwith URL: %s", - imageName, fileURL); - bwidth = bimg.getWidth(); - bheight = bimg.getHeight(); - bsize = bimg.getData().getDataBuffer().getSize(); - currentMemory += bsize; - Image first; - while (images.size() > 0 && currentMemory > maxMemory) { - first = images.remove(0); - currentMemory -= first.bsize; - } - images.add(this); - log(lvl, "loaded %s (%d KB of %d MB (%d / %d %%) (%d))", imageName, (int) (bsize / KB), - (int) (maxMemory / MB), images.size(), (int) (100 * currentMemory / maxMemory), - (int) (currentMemory / KB)); - } else { - log(-1, "ImageName invalid! not cached!"); - } - } - return bimg; - } - /** * Internal Use: IDE: to get rid of cache entries at script close or save as * * @param bundlePath */ public static void purge(String bundlePath) { - URL pathURL = FileManager.makeURL(bundlePath); - if (!ImagePath.getPaths().get(0).pathURL.equals(pathURL)) { - log(-1, "purge: not current bundlepath: " + pathURL); + if (imageFiles.size() == 0) { return; } - int onPath = 0; - for (ImagePath.PathEntry e : ImagePath.getPaths()) { - if (e.pathURL.equals(pathURL)) { - onPath += 1; - } - } - if (onPath > 1 || imageFiles.size() == 0 ) { - // also added as path entry by import, so we do not purge + URL pathURL = FileManager.makeURL(bundlePath); + if (!ImagePath.getPaths().get(0).pathURL.toExternalForm().equals(pathURL.toExternalForm())) { + log(-1, "purge: not current bundlepath: " + pathURL); return; } - String pathStr = pathURL.toString(); + String pathStr = pathURL.toExternalForm(); URL imgURL; Image img; - Image buf; log(lvl, "purge: " + pathStr); Iterator<Map.Entry<URL, Image>> it = imageFiles.entrySet().iterator(); Map.Entry<URL, Image> entry; @@ -370,26 +387,34 @@ public class Image { while (it.hasNext()) { entry = it.next(); imgURL = entry.getKey(); - if (imgURL.toString().startsWith(pathStr)) { + if (imgURL.toExternalForm().startsWith(pathStr)) { log(lvl, "purge: entry: " + imgURL.toString()); purgeList.add(entry.getValue()); it.remove(); - } } if (purgeList.size() > 0) { - bit = images.iterator(); - while (bit.hasNext()) { - buf = bit.next(); - if (purgeList.contains(buf)) { - bit.remove(); - log(lvl, "purge: bimg: " + buf); - currentMemory -= buf.bsize; - } + bit = images.iterator(); + while (bit.hasNext()) { + img = bit.next(); + if (purgeList.contains(img)) { + bit.remove(); + log(lvl, "purge: bimg: " + img); + currentMemory -= img.bsize; } - log(lvl, "Max %d MB (%d / %d %%) (%d))", (int) (maxMemory / MB), images.size(), - (int) (100 * currentMemory / maxMemory), (int) (currentMemory / KB)); + } } + log(lvl, "After Purge (%d): Max %d MB (%d / %d %%) (%d))", + purgeList.size(), (int) (maxMemory / MB), images.size(), + (int) (100 * currentMemory / maxMemory), (int) (currentMemory / KB)); + } + + /** + * Get the image's descriptive name + * + */ + public String getName() { + return imageName; } public ImageGroup getGroup() { @@ -417,12 +442,20 @@ public class Image { return imageIsAbsolute; } + protected void setIsAbsolute(boolean val) { + imageIsAbsolute = val; + } + + /** + * + * @return true + */ protected boolean isText() { return imageIsText; } - protected void setIsText(boolean isText) { - imageIsText = isText; + protected void setIsText(boolean val) { + imageIsText = val; } /** @@ -434,7 +467,8 @@ public class Image { } /** - * @return the image's absolute filename or null if jar, http or in memory image + * @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())) { @@ -444,14 +478,6 @@ public class Image { } /** - * Get the image's descriptive name - * - */ - public String getName() { - return imageName; - } - - /** * return the image's BufferedImage (load it if not in cache) * */ @@ -478,6 +504,7 @@ public class Image { /** * if the image was already found before + * * @return the rectangle where it was found */ public Rectangle getLastSeen() { @@ -486,6 +513,7 @@ public class Image { /** * if the image was already found before + * * @return the similarity score */ public double getLastSeenScore() { @@ -494,6 +522,7 @@ public class Image { /** * Internal Use: set the last seen info after a find + * * @param lastSeen * @param sim */ @@ -505,8 +534,22 @@ public class Image { } } + public BufferedImage resize(float factor) { + int type = 0; + BufferedImage bimg = get(); + type = bimg.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : bimg.getType(); + int width = (int) (getSize().getWidth() * factor); + int height = (int) (getSize().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 * @param y * @param w @@ -523,6 +566,7 @@ public class Image { /** * create a sub image from this image + * * @param part (the constants Region.XXX as used with region.get()) * @return */ @@ -534,6 +578,7 @@ public class Image { /** * store info: this image is divided vertically into n even rows <br> * a preparation for using getRow() + * * @param n * @return the top row */ @@ -544,6 +589,7 @@ public class Image { /** * store info: this image is divided horizontally into n even columns <br> * a preparation for using getCol() + * * @param n * @return the leftmost column */ @@ -586,6 +632,7 @@ public class Image { /** * store info: this image is divided into a raster of even cells <br> * a preparation for using getCell() + * * @param r * @param c * @return the top left cell @@ -594,20 +641,21 @@ public class Image { rows = r; cols = c; if (r > 0) { - rowH = (int) (getSize().height/r); - rowHd = getSize().height - r*rowH; + rowH = (int) (getSize().height / r); + rowHd = getSize().height - r * rowH; } if (c > 0) { - colW = (int) (getSize().width/c); - colWd = getSize().width - c*colW; + 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 + * 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 * @return the row as new image or the image itself, if no rows are setup */ @@ -619,16 +667,18 @@ public class Image { r = rows + r; } r = Math.max(0, r); - r = Math.min(r, rows-1); + 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 + * negative counts reverse from the end (last = -1) values outside range are 0 + * or last respectively + * * @param c - * @return the column as new image or the image itself, if no columns are setup + * @return the column as new image or the image itself, if no columns are + * setup */ public Image getCol(int c) { if (cols == 0) { @@ -638,20 +688,25 @@ public class Image { c = cols + c; } c = Math.max(0, c); - c = Math.min(c, cols-1); + 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 <br> - * negative counts reverse from the end (last = -1) - * values outside range are 0 or last respectively + * negative counts reverse from the end (last = -1) values outside range are 0 + * or last respectively + * * @param c * @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) { + return getCol(c); + } + if (cols == 0) { + return getRow(r); + } if (rows == 0 && cols == 0) { return this; } @@ -662,9 +717,9 @@ public class Image { c = cols - c; } r = Math.max(0, r); - r = Math.min(r, rows-1); + r = Math.min(r, rows - 1); c = Math.max(0, c); - c = Math.min(c, cols-1); + c = Math.min(c, cols - 1); return getSub(c * colW, r * rowH, colW, rowH); } diff --git a/API/src/main/java/org/sikuli/script/ImagePath.java b/API/src/main/java/org/sikuli/script/ImagePath.java index fc2b5c8..5568cdb 100644 --- a/API/src/main/java/org/sikuli/script/ImagePath.java +++ b/API/src/main/java/org/sikuli/script/ImagePath.java @@ -15,8 +15,8 @@ import org.sikuli.basics.SikuliScript; public class ImagePath { - private static String me = "ImagePath"; - private static int lvl = 3; + private static final String me = "ImagePath"; + private static final int lvl = 3; private static void log(int level, String message, Object... args) { Debug.logx(level, "", me + ": " + message, args); @@ -32,7 +32,7 @@ public class ImagePath { } } - private static List<PathEntry> imagePaths = Collections.synchronizedList(new ArrayList<PathEntry>()); + private static final List<PathEntry> imagePaths = Collections.synchronizedList(new ArrayList<PathEntry>()); private static String BundlePath = null; static { @@ -42,11 +42,34 @@ public class ImagePath { /** * get the list of path entries + * @return */ public static List<PathEntry> getPaths() { return imagePaths; } + private static int getPathCount() { + int count = imagePaths.size(); + for (PathEntry path : imagePaths) { + if (path == null) { + count--; + } + } + return count; + } + + public static String[] getImagePath() { + String[] paths = new String[getPathCount()]; + int i = 0; + for (PathEntry path : imagePaths) { + if (path == null) { + continue; + } + paths[i++] = path.pathGiven; + } + return paths; + } + /** * print the list of path entries */ @@ -87,6 +110,9 @@ public class ImagePath { if (fURL != null) { break; } + } else { + //TODO support for http image path + log(-1, "URL not supported: " + path.pathURL); } } if (fURL == null) { @@ -133,6 +159,7 @@ public class ImagePath { * see; {@link #add(String, String)} * * @param mainPath a valid classname optionally followed by /subfolder... + * @return */ public static boolean add(String mainPath) { return add(mainPath, null); @@ -146,18 +173,39 @@ public class ImagePath { * * @param mainPath a valid classname optionally followed by /subfolder... * @param altPath alternative image folder, when not running from jar (absolute path) + * @return */ public static boolean add(String mainPath, String altPath) { PathEntry path = makePathURL(mainPath, altPath); if (path != null) { - log(lvl, "addImagePath: %s", path.pathURL); - imagePaths.add(path); + if (hasPath(path.pathURL) < 0) { + log(lvl, "add: %s", path.pathURL); + imagePaths.add(path); + } else { + log(lvl, "duplicate not added: %s", path.pathURL); + } return true; } else { log(-1, "addImagePath: not valid: %s", mainPath); return false; } } + + private static int hasPath(URL pURL) { + PathEntry path = imagePaths.get(0); + if (path == null) { + return -1; + } + if (path.pathURL.toExternalForm().equals(pURL.toExternalForm())) { + return 0; + } + for (PathEntry p : imagePaths.subList(1, imagePaths.size())) { + if (p!= null && p.pathURL.toExternalForm().equals(pURL.toExternalForm())) { + return 1; + } + } + return -1; + } /** * add entry to end of list -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/sikuli.git _______________________________________________ pkg-java-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits

