com.sikulix.api.Image.java Source code

Java tutorial

Introduction

Here is the source code for com.sikulix.api.Image.java

Source

/*
 * Copyright (c) 2016 - sikulix.com - MIT license
 */

package com.sikulix.api;

import com.sikulix.core.SX;
import com.sikulix.core.SXLog;
import com.sikulix.core.Visual;
import org.opencv.core.*;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;
import java.util.*;
import java.util.List;

public class Image extends Visual {

    private static vType vClazz = vType.IMAGE;
    private static SXLog log = SX.getLogger("SX." + vClazz.toString());

    //<editor-fold desc="*** construction">
    public Image() {
        clazz = vClazz;
        init(0, 0, 0, 0);
    }

    public Image(BufferedImage bimg) {
        this();
        content = makeMat(bimg);
        init(0, 0, content.width(), content.height());
    }

    public Image(BufferedImage bimg, Rectangle rect) {
        this();
        content = makeMat(bimg);
        init(rect.x, rect.y, content.width(), content.height());
    }

    public Image(Mat mat) {
        this();
        content = mat;
        init(0, 0, content.width(), content.height());
    }

    public Image(String fpImg) {
        this();
        content = get(fpImg);
        init(0, 0, content.width(), content.height());
    }

    public Image(URL url) {
        this();
        content = get(url);
        init(0, 0, content.width(), content.height());
    }

    public Image(Visual vis) {
        //TODO implement Image(Visual vis)
        this();
    }

    /**
     * @return true if the Visual is useable and/or has valid content
     */
    @Override
    public boolean isValid() {
        return !content.empty();
    }

    public boolean isText() {
        //TODO implement isText()
        return false;
    }
    //</editor-fold>

    //<editor-fold desc="*** get content">
    private URL urlImg = null;

    private Mat get(String fpImg) {
        URL url = getURL(fpImg);
        return get(url);
    }

    private Mat get(URL url) {
        Mat mContent = new Mat();
        if (SX.isSet(url)) {
            urlImg = url;
            if (isCaching()) {
                mContent = imageFiles.get(urlImg);
                if (SX.isNull(mContent)) {
                    mContent = new Mat();
                }
            }
            if (mContent.empty()) {
                mContent = get();
            }
            if (isCaching() && !mContent.empty()) {
                changeCache(true, urlImg, content);
            }
        }
        return mContent;
    }

    private URL getURL(String fpImg) {
        URL url = null;
        //TODO implement path search
        return url;
    }

    private Mat get() {
        Mat mContent = new Mat();
        if (urlImg != null) {
            File imgFile = new File(urlImg.getPath());
            mContent = Highgui.imread(imgFile.getAbsolutePath());
            if (!mContent.empty()) {
                log.debug("get: loaded: (%dx%s) %s", mContent.width(), mContent.height(), urlImg);
            } else {
                log.error("get: not loaded: %s", urlImg);
            }
        }
        return mContent;
    }

    public Image reset() {
        if (isCaching() && isValid()) {
            changeCache(false, urlImg, content);
        }
        content = get();
        if (isCaching() && !content.empty()) {
            changeCache(true, urlImg, content);
        }
        return this;
    }
    //</editor-fold>

    //<editor-fold desc="*** cache">
    private static final Map<URL, Mat> imageFiles = Collections.synchronizedMap(new HashMap<URL, Mat>());
    private static long currentMemory = 0;

    private static int ImageCache = 64;
    private static final int KB = 1024;
    private static final int MB = KB * KB;

    /**
     * set the maximum to be used for the {@link Image} cache
     * <br>the start up value is 64 (meaning MB)
     * <br>using 0 switches off caching and clears the cache in that moment
     *
     * @param max cache size in MB
     */
    public static void setImageCache(int max) {
        ImageCache = max;
        if (max == 0) {
            changeCache(null, null, null);
        }
    }

    private static boolean isCaching() {
        return ImageCache > 0;
    }

    private synchronized static void changeCache(Boolean upOrDown, URL urlImg, Mat mat) {
        if (SX.isNotSet(upOrDown) || ImageCache < currentMemory + getMatSize(mat)) {
            for (URL url : imageFiles.keySet()) {
                imageFiles.put(url, new Mat());
            }
            currentMemory = 0;
            if (SX.isNotSet(upOrDown)) {
                return;
            }
        }
        if (upOrDown) {
            imageFiles.put(urlImg, mat);
            currentMemory += getMatSize(mat);
        } else {
            imageFiles.put(urlImg, new Mat());
            currentMemory -= getMatSize(mat);
            currentMemory = currentMemory < 0 ? 0 : currentMemory;
        }
    }

    private static long getMatSize(Mat mat) {
        return mat.channels() * mat.width() * mat.height();
    }
    //</editor-fold>

    //<editor-fold desc="*** path">
    private static final List<URL> imagePath = Collections.synchronizedList(new ArrayList<URL>());

    private static void initPath() {
        if (imagePath.isEmpty()) {
            imagePath.add(SX.getFileURL(SX.getSXIMAGES()));
        }
    }

    public static boolean setBundlePath(Object... args) {
        initPath();
        if (args.length == 0) {
            imagePath.set(0, SX.getFileURL(SX.getSXIMAGES()));
            return true;
        }
        URL urlPath = SX.makeURL(args);
        if (SX.isSet(urlPath)) {
            imagePath.set(0, urlPath);
            return true;
        }
        return false;
    }

    public static boolean resetPath(Object... args) {
        imagePath.clear();
        if (args.length == 0) {
            initPath();
            return true;
        }
        return setBundlePath(args);
    }

    public static String getBundlePath() {
        initPath();
        return SX.makePath(imagePath.get(0));
    }

    public static String[] getPath(String filter) {
        String[] sPaths = new String[imagePath.size()];
        int n = 0;
        String sPath;
        for (URL uPath : imagePath) {
            sPath = SX.makePath(uPath);
            if (SX.isSet(filter) && !sPath.contains(filter)) {
                continue;
            }
            sPaths[n++] = sPath;
        }
        return sPaths;
    }

    public static String[] getPath() {
        return getPath("");
    }

    public static int hasPath(String filter) {
        if (SX.isNotSet(filter))
            return -1;
        int n = 0;
        String sPath;
        for (URL uPath : imagePath) {
            sPath = SX.makePath(uPath);
            if (sPath.contains(filter)) {
                return n;
            }
            n++;
        }
        return -1;
    }

    public static String getPath(int n) {
        if (n < 0 || n > imagePath.size() - 1) {
            n = 0;
        }
        return SX.makePath(imagePath.get(n));
    }

    public static boolean setPath(int n, String fpPath) {
        if (n < 0 || n > imagePath.size() - 1) {
            return false;
        }
        URL urlPath = SX.makeURL(fpPath);
        if (SX.isSet(urlPath)) {
            imagePath.set(n, urlPath);
            return true;
        }
        return false;
    }

    public static String removePath(int n) {
        if (n < 0 || n > imagePath.size() - 1) {
            n = 0;
        }
        return SX.makePath(imagePath.get(n));
    }

    public static int addPath(Object... args) {
        log.error("//TODO addPath: not implemented");
        URL url = null;
        return -1;
    }

    public static int removePath(Object... args) {
        //TODO implment removePath()
        URL url = null;
        return -1;
    }

    public static int insertPath(Object... args) {
        //TODO implment insertPath()
        URL url = null;
        return -1;
    }

    public static int changePath(Object... args) {
        //TODO implment changePath()
        URL url = null;
        return -1;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="*** helpers">
    private final int resizeMinDownSample = 12;
    private double resizeFactor;
    private boolean plainColor = false;
    private boolean blackColor = false;

    public boolean isPlainColor() {
        return plainColor;
    }

    public boolean isBlack() {
        return blackColor;
    }

    private void checkProbe() {
        resizeFactor = Math.min(((double) content.width()) / resizeMinDownSample,
                ((double) content.height()) / resizeMinDownSample);
        resizeFactor = Math.max(1.0, resizeFactor);

        MatOfDouble pMean = new MatOfDouble();
        MatOfDouble pStdDev = new MatOfDouble();
        Core.meanStdDev(content, pMean, pStdDev);
        double min = 1.0E-5;
        plainColor = false;
        double sum = 0.0;
        double[] arr = pStdDev.toArray();
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        if (sum < min) {
            plainColor = true;
        }
        sum = 0.0;
        arr = pMean.toArray();
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        if (sum < min && plainColor) {
            blackColor = true;
        }
    }

    public double getResizeFactor() {
        return resizeFactor;
    }

    /**
     * resize the image's CV-Mat by factor
     *
     * @param factor resize factor
     * @return a new inMemory Image
     */
    public Image resize(float factor) {
        Image img = new Image();
        if (isValid()) {
            Mat newMat = new Mat();
            Size newS = new Size(w * factor, h * factor);
            Imgproc.resize(content, newMat, newS, 0, 0, Imgproc.INTER_AREA);
            img = new Image(newMat);
        }
        return img;
    }

    /**
     * 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) {
        Image img = new Image();
        if (isValid()) {
            img = new Image(content.submat(new Rect(x, y, w, h)));
        }
        return img;
    }

    protected static String getValidImageFilename(String fname) {
        String validEndings = ".png.jpg.jpeg.tiff.bmp";
        String defaultEnding = ".png";
        int dot = fname.lastIndexOf(".");
        String ending = defaultEnding;
        if (dot > 0) {
            ending = fname.substring(dot);
            if (validEndings.contains(ending.toLowerCase())) {
                return fname;
            }
        } else {
            fname += ending;
            return fname;
        }
        return "";
    }

    public String save(String name) {
        String fpName = getValidImageFilename("_" + name);
        File fName = new File(getBundlePath(), fpName);
        Highgui.imwrite(fName.getAbsolutePath(), content);
        return fpName;
    }
    //</editor-fold>
}