Ask Your Question

Lucky Luke's profile - activity

2016-06-13 09:33:21 -0600 received badge  Popular Question (source)
2015-05-09 04:56:43 -0600 received badge  Famous Question (source)
2014-06-30 07:32:48 -0600 received badge  Notable Question (source)
2014-02-20 01:54:48 -0600 received badge  Popular Question (source)
2014-01-17 03:39:36 -0600 commented answer Opencv java - Load image to GUI

Please take note of Daniel's solution (using System.arraycopy) which is again faster than this.

2014-01-17 03:38:07 -0600 commented answer Opencv java - Load image to GUI

This is indeed faster than the solution I've posted above (grayscale conversion only marginally, but color images are converted roughly about twice as fast).

Thanks for posting an even better solution.

2013-09-22 17:47:02 -0600 received badge  Good Answer (source)
2013-09-22 17:47:02 -0600 received badge  Enlightened (source)
2013-09-10 06:33:25 -0600 commented question Switching from BRISK to FREAK descriptor causes only wrong matches

Given a default patternScale of 22, 50 is certainly not too small (my remark about patternScale was really just a guess; as in shooting from the hips).

Maybe someone that actually had success with FREAK could offer some insight...

2013-09-09 20:40:45 -0600 commented question Switching from BRISK to FREAK descriptor causes only wrong matches

Up to now (novice here) I've made similar experiences: the brisk descriptors are fantastic (if you take computation time into account too, it beats SIFT/SURF/et.al. any day), while I get only rubbish results with the FREAK descriptors; no matter what detector I use it with (trying to understand the paper and adjusting the parameters to my needs didn't help either... My guess is that FREAK easily fails with too small patternScale; yet that's what I need for my purposes, since larger patternScales result in too few detected keypoints while operating with rather small train images...).

The only tip I can offer: try to use the ORB-detector with the BRISK-extractor, which works really nice and yeah; the BRISK-detector seems to be bugged (or it's really one hell of a memory hog).

2013-07-31 10:40:02 -0600 received badge  Student (source)
2013-05-22 09:26:08 -0600 received badge  Nice Answer (source)
2013-04-21 08:01:14 -0600 commented answer Color (>2-channel) Histogram, Java API

Ahh, I see. Well, in this case it's a non-issue, since I can totally live with 2-dimensional histograms (especially in the realms of color).

Yet it makes me wonder if I will eventually run into this sort of problem (no higher dimensional matrices for the java guys...) elsewhere (guess I'll learn soon enough...).

So long and thanks for all the fish! :)

2013-04-19 22:51:22 -0600 asked a question Color (>2-channel) Histogram, Java API

In short: why would Imgproc.calcHist work on 2, but not on 3 (or more) channels/dimensions? Is this a bug? If so, has it already been reported (someone working on this)? Can we expect a fix any time soon? And if not - or regardless - what are my options to deal with this? Any suggestions/workarounds?

In full; the following code:

public class Histogram3D {
    private List<Mat> images;
    private MatOfInt channels;
    private Mat mask;
    private Mat hist;
    private MatOfInt histSize;
    private MatOfFloat ranges;

    public Histogram3D() {
        histSize = new MatOfInt(256, 256, 256);
        ranges = new MatOfFloat(0.0f, 255.0f,
                                0.0f, 255.0f,
                                0.0f, 255.0f); 
        channels = new MatOfInt(0, 1, 2);
        mask = new Mat();
        hist = new Mat();
    }

    public void computeHistogram(Mat image) {
        images = new ArrayList<>();
        images.add(image);

        Imgproc.calcHist(images, channels, mask, hist, histSize, ranges);
    }
    // ...

...results with a hist of type -1x-1xCV_32FC1. While I don't get an error or anything, this is just garbage. :(

But then, on the other hand, the following code:

public class Histogram3D {
    private List<Mat> images;
    private MatOfInt channels;
    private Mat mask;
    private Mat hist;
    private MatOfInt histSize;
    private MatOfFloat ranges;

    public Histogram3D() {
        histSize = new MatOfInt(256, 256);
        ranges = new MatOfFloat(0.0f, 255.0f,
                                //0.0f, 255.0f,
                                0.0f, 255.0f); 
        channels = new MatOfInt(0, 1);
        mask = new Mat();
        hist = new Mat();
    }

    public void computeHistogram(Mat image) {
        images = new ArrayList<>();
        images.add(image);

        Imgproc.calcHist(images, channels, mask, hist, histSize, ranges);
    }
    // ...

...ends up with a flawless histogram of type 256x256xCV_32FC1. Hmm, granted, how should hist even look like with 3 dimensions? 256x256x256xCV_32FC1 does not compute. I've never seen a 256x256xCV_32FC256-Mat before... How about (256x256)x256xCV_32FC1 maybe? A list/an array of 256 256x256xCV_32FC1 would sound reasonable, but the methods signature wouldn't fit that any longer? ...

So... what are my options to compute my 3-channel/dimensional histogram? (But ok, if my concern is a "color histogram", I might wanna check out other color spaces...)

Thanks. :)

2013-03-29 13:53:51 -0600 received badge  Critic (source)
2013-03-29 09:09:28 -0600 received badge  Organizer (source)
2013-03-29 08:47:31 -0600 received badge  Teacher (source)
2013-03-29 07:29:10 -0600 answered a question Opencv java - Load image to GUI

While I'm not sure the following is the most efficient (or even elegant) solution, it works and is definitely better than this "writing to disk..., read from the disk again"-approach:

/**
 * Converts/writes a Mat into a BufferedImage.
 * 
 * @param bgr Mat of type CV_8UC3 or CV_8UC1
 * @return BufferedImage of type TYPE_INT_RGB or TYPE_BYTE_GRAY
 */
public static BufferedImage matToBufferedImage(Mat bgr) {
    int width = bgr.width();
    int height = bgr.height();
    BufferedImage image;
    WritableRaster raster;

    if (bgr.channels()==1) {
        image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
        raster = image.getRaster();

        byte[] px = new byte[1];

        for (int y=0; y<height; y++) {
            for (int x=0; x<width; x++) {
                bgr.get(y,x,px);
                raster.setSample(x, y, 0, px[0]);
            }
        }
    } else {
        image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        raster = image.getRaster();

        byte[] px = new byte[3];
        int[] rgb = new int[3];

        for (int y=0; y<height; y++) {
            for (int x=0; x<width; x++) {
                bgr.get(y,x,px);
                rgb[0] = px[2];
                rgb[1] = px[1];
                rgb[2] = px[0];
                raster.setPixel(x,y,rgb);
            }
        }
    }

    return image;
}

EDIT

Ahh, thanks to an earlier post by Alexander Smorkalov (see http://answers.opencv.org/question/6281/is-there-a-way-to-grab-all-the-pixels-of-a-matrix/?answer=6286#post-id-6286 ) I've figured out to do this much faster by retrieving all image data in one junk.

My first attempt with ImageIO.read given an ByteArrayInputStream of said data yielded promising results at first (6 to 7 times faster), but right after posting the method, that piece of code didn't even work any longer (no idea why it did work at some point earlier). Think this has something to do with unavailable/uninitialized ImageReader or (sorry, I'm not too confident with Java and get easily lost...).

Anyways, a second attempt (without ImageIO and ByteArrayInputStreams and what not) yielded even better results. Observe:

/**
 * Converts/writes a Mat into a BufferedImage.
 * 
 * @param matrix Mat of type CV_8UC3 or CV_8UC1
 * @return BufferedImage of type TYPE_3BYTE_BGR or TYPE_BYTE_GRAY
 */
public static BufferedImage matToBufferedImage(Mat matrix) {
    int cols = matrix.cols();
    int rows = matrix.rows();
    int elemSize = (int)matrix.elemSize();
    byte[] data = new byte[cols * rows * elemSize];
    int type;

    matrix.get(0, 0, data);

    switch (matrix.channels()) {
        case 1:
            type = BufferedImage.TYPE_BYTE_GRAY;
            break;

        case 3: 
            type = BufferedImage.TYPE_3BYTE_BGR;

            // bgr to rgb
            byte b;
            for(int i=0; i<data.length; i=i+3) {
                b = data[i];
                data[i] = data[i+2];
                data[i+2] = b;
            }
            break;

        default:
            return null;
    }

    BufferedImage image = new BufferedImage(cols, rows, type);
    image.getRaster().setDataElements(0, 0, cols, rows, data);

    return image;
}

Quick benchmark results, comparing this new method to the initially posted one (converting a 640x480 Mat to BufferedImage, 10 times, then taking the average):

  • with a grayscale (8-bit, 1 channels) mat: 127 times faster
  • with a bgr (8-bit, 3 channels) mat: 15 times faster

Not surprisingly this is much faster than the first version (and also faster than my ImageIO experiments). But I think we might even do better, by getting rid of that i=i+3 loop which swaps the R ... (more)

2013-03-28 20:39:29 -0600 received badge  Editor (source)
2013-03-28 16:59:10 -0600 received badge  Supporter (source)
2013-03-28 16:57:46 -0600 commented answer OpenCV Java API: Highgui.imread() and paths pointing to files in a jar

Wouldn't you argue that imread() should be able to? Or is this rather a trade off we have to live with due to the automatic generation of the Java API?

As for your conclusion (keeping resources apart from the code in the jar): I don't think that's acceptable. I think StevenPuttemans is on the right/better track... guess I will write an OpenCVUtils class, that will read the image in Java and manually create the Mat for the case that imread returned an empty Mat, but the file has been found (by Java) otherwise... (that's where I'd argue that this part should have been already put into imread, no?)

-- (Edit: solution added to the original post)

2013-03-27 14:58:34 -0600 asked a question OpenCV Java API: Highgui.imread() and paths pointing to files in a jar

The following scenario:

    URL img_url = getClass().getResource("/resources/some-image.jpg");
    String img_path = img_url.getPath();

    if (img_path.startsWith("/")) {
        img_path = img_path.substring(1);
    }

    Mat img = Highgui.imread(img_path);

...works fine running from NetBeans (i.e. unpacked/not built). But once I build a jar and try to run it, things are falling appart. While the call to getResource(), and in consequence to getPath(), both are fine (i.e. the file is found), the OpenCV function Highgui.imread() returns an empty Mat now.

So I highly suspect that said OpenCV function can't handle path's that point into a jar file, which look something like: "file:/C:/.../file.jar!/resources/some-image.jpg"

Any ideas? :/

EDIT (v2; improved solution):

/**
* A utility class to get rid of annoying boilerplate code while dealing with
* the OpenCV Java API (which is generated automatically for the most part) and
* in consequence with lots of Mat's (I/O, conversions to BufferedImages and 
* what not, ...).
*/
public final class OpenCVUtils {

    /**
    * Don't let anyone instantiate this class.
    */
    private OpenCVUtils() {}

    /**
    * 8bit, 3-channel image.
    * @see Highgui.CV_LOAD_IMAGE_COLOR
    */
    public static final int LOAD_COLOR = Highgui.CV_LOAD_IMAGE_COLOR;

    /**
    * 8bit, 1-channel image.
    * @see Highgui.CV_LOAD_IMAGE_GRAYSCALE
    */
    public static final int LOAD_GRAYSCALE = Highgui.CV_LOAD_IMAGE_GRAYSCALE;

    /**
    * Loads an image from a file.
    * This is a wrapper around Highgui.imread() which fails if the file
    * is inside a jar/zip. This function takes care of that case, loads the
    * image in Java and manually creates the Mat...
    * 
    * @param name name of the resource 
    * @param int Flags specifying the color type of a loaded image;
    *            supported: LOAD_COLOR (8-bit, 3-channels), 
    *                       LOAD_GRAYSCALE (8-bit, 1-channel), 
    * @return Mat of type CV_8UC3 or CV_8UC1 (empty Mat is returned in case of an error)
    */
    public static Mat readImage(String name, int flags) {
        URL url = name.getClass().getResource(name);

        // make sure the file exists
        if (url == null) {
            System.out.println("ResourceNotFound: " + name);
            return new Mat();
        }

        String path = url.getPath();

        // not sure why we (sometimes; while running unpacked from the IDE) end 
        // up with the authority-part of the path (a single slash) as prefix,
        // ...anyways: Highgui.imread can't handle it, so that's why.
        if (path.startsWith("/")) {
            path = path.substring(1);
        }

        Mat image = Highgui.imread(path, flags);

        // ...and if Highgui.imread() has failed, we simply assume that the file 
        // is packed in a jar (i.e. Java should be able to read the image)
        if (image.empty()) {
            BufferedImage buf;

            try {
                buf = ImageIO.read(url);
            } catch (IOException e) {
                System.out.println("IOException: " + e.getMessage());
                return image;
            }

            int height = buf.getHeight();
            int width = buf.getWidth();
            int rgb, type, channels;

            switch (flags) {
                case LOAD_GRAYSCALE:
                    type = CvType.CV_8UC1;
                    channels = 1;
                    break;
                case LOAD_COLOR:
                default:
                    type = CvType.CV_8UC3;
                    channels = 3;
                    break;
            }

            byte[] px = new byte[channels];
            image = new Mat(height, width, type);

            for (int y=0; y<height; y++) {
                for (int x=0; x<width; x++) {
                    rgb = buf.getRGB(x, y);
                    px[0] = (byte)(rgb & 0xFF);
                    if (channels==3) {
                        px[1] = (byte)((rgb >> 8) & 0xFF);
                        px[2] = (byte)((rgb >> 16) & 0xFF);
                    }
                    image.put(y, x, px);
                }
            }            
        }

        return image;
    }

    /**
    * Loads an image from a file ...
(more)