Ask Your Question
2

OpenCV Java API: Highgui.imread() and paths pointing to files in a jar

asked 2013-03-27 14:58:34 -0600

Lucky Luke gravatar image

updated 2013-03-29 09:09:28 -0600

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)
edit retag flag offensive close merge delete

2 answers

Sort by ยป oldest newest most voted
2

answered 2013-03-27 15:17:44 -0600

berak gravatar image

well, you're right there, imread() can't peek into a .jar or a .zip

you'll have to keep your resources apart from the code in that jar

edit flag offensive delete link more

Comments

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)

Lucky Luke gravatar imageLucky Luke ( 2013-03-28 16:57:46 -0600 )edit

I can follow you that this is an interesting problem. But do not forget that the first focus of OpenCV is the C/C++ interface, than mobile platforms, than Java Desktop. So basically, there is a huge need of people porting things to Java and adding a pull request to add their code :)

StevenPuttemans gravatar imageStevenPuttemans ( 2013-03-29 02:17:47 -0600 )edit
1

answered 2013-03-27 16:43:55 -0600

No idea if this can help you out, but I have found some code on the internet, which seems to help you get resources from a jar file.

Toolkit tk = Toolkit.getDefaultToolkit(); 
URL url = getClass().getResource("path/to/img.png"); 
Image img = tk.createImage(url); 
tk.prepareImage(img, -1, -1, null);

Found it on this link:

http://stackoverflow.com/questions/2393194/how-to-access-resources-in-jar-file&usg=ALkJrhiexrVa5uRlPATjB4c3AsqKTUeZXw">http://translate.googleusercontent.com/translate_c?depth=1&ei=q2dTUdn7DKnJ0AXZv4CICA&hl=nl&prev=/search%3Fq%3DopenCV%2Bresources%2Binto%2Bjar%2Bfile%26hl%3Dnl%26biw%3D1440%26bih%3D773&rurl=translate.google.be&sl=en&u=http://stackoverflow.com/questions/2393194/how-to-access-resources-in-jar-file&usg=ALkJrhiexrVa5uRlPATjB4c3AsqKTUeZXw

edit flag offensive delete link more

Question Tools

Stats

Asked: 2013-03-27 14:58:34 -0600

Seen: 14,672 times

Last updated: Mar 29 '13