Categories
discuss

How to match the color models of BufferedImage and Mat?

I’m trying to convert BufferedImage to Mat for a large set of images with different file types downloaded from the internet. Because I am scraping the images from websites, I have no control over the file formats. I can easily load them to BufferedImage without knowing the format, but to convert them to Mat, I need to know the image type. Unfortunately, there doesn’t seem to be a nice correspondence between CvType and BufferedImage types.

CvType represents image types with the format CV_<bit-depth>{U|S|F}C<number_of_channels> where U is unsigned char, S is signed char, and F is float.

BufferedImage types have more variety in the representation including a symmetrical channels (TYPE_4BYTE_ABGR), varying numbers of bits (TYPE_BYTE_BINARY), and whatever an indexed byte image is (TYPE_BYTE_INDEXED).

Based on the documentation, I tried to complete my own correspondence.

BufferedImage imgBuffer = ImageIO.read(new File("example.gif"));

//Save file as reference
File outputfile = new File("temp/image.png");
ImageIO.write(imgBuffer, "png", outputfile);

//My correspondance
int curCVtype = -1;
switch (imgBuffer.getType()) {
case BufferedImage.TYPE_3BYTE_BGR:
    curCVtype = CvType.CV_8UC3;
    break;
case BufferedImage.TYPE_BYTE_GRAY:
    curCVtype = CvType.CV_8UC1;
    break;
case BufferedImage.TYPE_INT_BGR:
case BufferedImage.TYPE_INT_RGB:
    curCVtype = CvType.CV_8SC3;
    break;
case BufferedImage.TYPE_INT_ARGB:
case BufferedImage.TYPE_INT_ARGB_PRE:
    curCVtype = CvType.CV_8SC4;
    break;
default:
//  The types not handled by my correspondence
//                  BufferedImage.TYPE_BYTE_BINARY;
//                  BufferedImage.TYPE_USHORT_GRAY;
//                  BufferedImage.TYPE_4BYTE_ABGR;
//                  BufferedImage.TYPE_4BYTE_ABGR_PRE;
//                  BufferedImage.TYPE_BYTE_INDEXED;
//                  BufferedImage.TYPE_CUSTOM;

    System.out.println("Unsupported format:" + imgBuffer.getType());
    //Here I choose a default type
    curCVtype = CvType.CV_8SC3;
}

//Convert to Mat
byte[] pixels = ((DataBufferByte) imgBuffer.getRaster().getDataBuffer()).getData();
Mat img = new Mat(imgBuffer.getHeight(), imgBuffer.getWidth(), curCVtype);
img.put(0, 0, pixels);

//Write the output to compare
Imgcodecs.imwrite("temp/image_mat.png", img);

Questions

  1. Am I going about this correctly? Or is there a better approach?
  2. What are the correct correspondences for the types that are in my default case?

Examples

Input
Input example gif

Converted to PNG by BufferedImage
Reference BufferedImage

Output PNG after conversion to Mat. BufferedImage type wasTYPE_BYTE_INDEXED converted to CvType.CV_8SC3
Mat output

Output PNG after conversion to Mat. BufferedImage type wasTYPE_BYTE_INDEXED converted to CvType.CV_8UC3
Mat output

Resources:

My starting code came from Converting BufferedImage to Mat in opencv .

What I know about CvTypes came from What’s the difference between cvtype values in OPENCV? .

Answer

Thanks to Miki and haraldK for the helpful comments.

My solution for unknown image types retrieves the pixels in RGB format, and put them into a Mat of CvType.CV_8UC4 . Finally, reorder the channels using Core.mixChannels to the OpenCV prefered order: BGR(A).

This example only reorders the channels for the unknown image types, but all non BGR image types would need to be reordered.

BufferedImage imgBuffer = ImageIO.read(new File("example.gif"));

//Save image as reference
File outputfile = new File("temp/image.png");
ImageIO.write(imgBuffer, "png", outputfile);

//My correspondance

int curCVtype = CvType.CV_8UC4; //Default type
boolean supportedType = true;

switch (imgBuffer.getType()) {
case BufferedImage.TYPE_3BYTE_BGR:
    curCVtype = CvType.CV_8UC3;
    break;
case BufferedImage.TYPE_BYTE_GRAY:
case BufferedImage.TYPE_BYTE_BINARY:
    curCVtype = CvType.CV_8UC1;
    break;
case BufferedImage.TYPE_INT_BGR:
case BufferedImage.TYPE_INT_RGB:
    curCVtype = CvType.CV_32SC3;
    break;
case BufferedImage.TYPE_INT_ARGB:
case BufferedImage.TYPE_INT_ARGB_PRE:
    curCVtype = CvType.CV_32SC4;
    break;
case BufferedImage.TYPE_USHORT_GRAY:
    curCVtype = CvType.CV_16UC1;
    break;
case BufferedImage.TYPE_4BYTE_ABGR:
case BufferedImage.TYPE_4BYTE_ABGR_PRE:
    curCVtype = CvType.CV_8UC4;
    break;
default:
    // BufferedImage.TYPE_BYTE_INDEXED;
    // BufferedImage.TYPE_CUSTOM;
    System.out.println("Unsupported format:" + imgBuffer.getType());
    supportedType = false;
}

//Convert to Mat
Mat img = new Mat(imgBuffer.getHeight(), imgBuffer.getWidth(), curCVtype);
if (supportedType) {
    // Insert pixel buffer directly
    byte[] pixels = ((DataBufferByte) imgBuffer.getRaster().getDataBuffer()).getData();
    img.put(0, 0, pixels);
} else {
    // Convert to RGB first
    int height = imgBuffer.getHeight();
    int width = imgBuffer.getWidth();
    int[] pixels = imgBuffer.getRGB(0, 0, width - 1, height - 1, null, 0, width);

    // Convert ints to bytes
    ByteBuffer byteBuffer = ByteBuffer.allocate(pixels.length * 4);
    IntBuffer intBuffer = byteBuffer.asIntBuffer();
    intBuffer.put(pixels);

    byte[] pixelBytes = byteBuffer.array();

    img.put(0, 0, pixelBytes);

    // Reorder the channels for Opencv BGRA format from
    // BufferedImage ARGB format
    Mat imgMix = img.clone();
    ArrayList<Mat> imgSrc = new ArrayList<Mat>();
    imgSrc.add(imgMix);

    ArrayList<Mat> imgDest = new ArrayList<Mat>();
    imgDest.add(img);

    int[] fromTo = { 0, 3, 1, 2, 2, 1, 3, 0 }; //Each pair is a channel swap
    Core.mixChannels(imgSrc, imgDest, new MatOfInt(fromTo));
}

//Save output image
Imgcodecs.imwrite("temp/image_mat.png", img);

The new output image
Output image

Categories
discuss

Android NfcV read tag always prepend 0x00

I got a problem with NFC-V tag reading. The tag type is Tag-it HF-I Plus (TMS37112). Here is the code I use to read data:

private void GetTagInfo(Tag tag){
        String[] techList = tag.GetTechList();
        for (int i = 0; i < techList.Length; i++) {
            if(techList[i].Contains("NfcV")){
                NfcV nfcv = NfcV.Get (tag);
                nfcv.Connect ();

                var response = nfcv.Transceive(new byte[] {
                    (byte)0x00,
                    (byte)0x23,
                    (byte)0x00,
                    (byte)0x01 });
            }
        }
    }

Writing in c# but not the purpose here (working on Xamarin).

Regardless of what I use as the first block number, I got an 0x00 before my data. Is this normal?

Answer

What you see is the flags byte. This byte is part of every NFC-V response frame and provides information about the command status. If this byte is 0x00 (or possibly 0x80), then the command was executed successfully and the remaining bytes contain the response parameters/data for your command (in your case one block starting at block zero requested by the READ MULTIPLE BLOCKS command).

If bit 0 of the flags byte is set, this indicates an execution error, the second byte will encode error information as defined in the ISO/IEC 15693-3 standard.

Thus a typical NFC-V command frame (when exchanged using NfcV.transceive()) looks like this:

+-------+--------------+--------------------------------+-------------------------+
| FLAGS | COMMAND CODE | [ADDRESS, if Addressed_flag=1] | COMMAND PARAMETERS/DATA |
+-------+--------------+--------------------------------+-------------------------+

and the response frame looks like this:

+-------+--------------------------+
| FLAGS | RESPONSE PARAMETERS/DATA |
+-------+--------------------------+
Categories
discuss

How do you simulate a null safe operator with a default return value?

I’m sorry for the title but I cant find a good way to describe the problem in one sentence. In short, I have a lot of Java code following this pattern

if (obj != null && obj.getPropertyX() != null) {
    return obj.getPropertyX();
}
return defaultProperty;

which can be rewritten as

return obj != null && obj.getPropertyX() != null ? obj.getPropertyX() : defaultProperty;

It’s still ugly and I’m wondering if there is some API in Google Guava or other library to help clean up this code. Specifically, I’m looking for something like

return someAPI(obj, "getPropertyX", defaultProperty);

I can implement this method using reflection but I’m not sure if that’s the proper way to do it. Thanks.

Answer

In Java 8, you could use:

return Optional.ofNullable(obj).map(Obj::getPropertyX).orElse(defaultProperty);
Categories
discuss

Intl.DateTimeFormat options hash: Getting leading zeros with ‘2-digit’

Intl.DateTimeFormat('en-US', {
    weekday: 'long',
    year: 'numeric',
    month: 'long',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit'
}).format(807959700000)

I expected the above call to return something like Wednesday, August 09, 1995, 04:15:00 AM but it seems the leading zero for the hour is missing. I get Wednesday, August 09, 1995, 4:15:00 AM

2-digit does not do the trick, though it seems to work for the day of the month. Does 2-digit mean something else than I expect, or am I doing something wrong?

P.S. I tested this in the Chrome console, nowhere else.

Answer

This will work if you set hour12 property to false. i.e.

Intl.DateTimeFormat('en-US', {
    weekday: 'long',
    year: 'numeric',
    month: 'long',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false     // setting 24 hour format 
}).format(807959700000);

It works for both 2am and also 2pm(14hr). But I know the same should work for 12 hour format too, but it is not. I checked in both chrome and firefox browsers.

When I checked the specs, it has defined an algorithm which can be used to implement Intl.DateTimeFormat functionality. I saw there were many special cases handled when hour12 property to set true, and one of the last step is

  1. If dateTimeFormat has an internal property [[hour12]] whose value is true, then
    • If pm is true, then let fv be an implementation and locale dependent String value representing “post meridiem”; else let fv be an implementation and locale dependent String value representing “ante meridiem”.
    • Replace the substring of result that consists of “{ampm}”, with fv.

So I think, the Intl.DateTimeFormat initially works with date object, later at step(8), it applies this step to put am/pm information.

During this step, may be 2-digits information specified in Intl.DateTimeFormat is not considered, but it has to be. I think this is a valid bug and it is already raised https://code.google.com/p/chromium/issues/detail?id=527926.


PS: I’m not saying issue is in specs, as described in the ECMAScript Language Specification,

Algorithms are used to precisely specify the required semantics of ECMAScript constructs, but are not intended to imply the use of any specific implementation technique.

Categories
discuss

Android Studio heap snapshot analyzer – what does “Dominating size” represent?

After dumping heap in Android Studio and viewing the created snapshot, there’s a field called “dominating size”.
What does it stand for?
enter image description here

Answer

That’s the size of that object + everything it keeps alive by direct or indirect references.

I’m not sure about Android Studio, but generally, “dominating” would mean that you only count the objects that are only accessible through your Bitmap objects here — i.e., if your Bitmap is freed, all of those objects could be freed as well.

In your case, each Bitmap object itself only takes 60 bytes — but they each have a (separately allocated) pixel buffer of some kind that they’re keeping alive.

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..