Machine Vision

cancel
Showing results for 
Search instead for 
Did you mean: 

How do you load signed 16 bit png file outside of labVIEW?

Solved!
Go to solution

I have a program that saves a signed 16 bit png file that I need to be able to load outside of labVIEW. 

Looking through the documentation for IMAQ write file 2 

dgagnon05_0-1659546009391.png

it looks like either the bias (if bit depth is false) or the MSB info should be stored in the PNG header.

I've tried loading the meta data in python but I'm not seeing any data related to either of these values. 

Where exactly in the header are these pieces of data stored and what are they called? 

 

Thanks! 

0 Kudos
Message 1 of 14
(3,729 Views)

You want to display a signed 16 bit PNG?  Have you looked into ImageJ?  https://imagej.net/software/imagej/

It's free and amazingly powerful.

0 Kudos
Message 2 of 14
(3,712 Views)

Hey thanks for your reply, I've tried opening the png files in imagej and encounter the same issue with negative values not being displayed correctly. At this point I'm more interested in trying to understand if there is any data in the png header that I can use to get the image back to it's original form. 

0 Kudos
Message 3 of 14
(3,679 Views)

I don't understand very well why you are trying to do that. All image standard are design to display positive numbers. Camera pixel can't accumulate negative charge and Computer display can't display negative pixel intensity.

But if you want to do that the simplest way is to add a constant to the whole image before you save.

For example if you have 8bit negative and positive numbers. Then change the image to 16bit. Add 256 to the image. That will make all your images positive.

You can display the 16bit image in multiple software. Tiff / PNG format will work.

If your images are already 16bit then you will need to find another method. You can create custom format and write viewer for it. 

Amit Shachaf
0 Kudos
Message 4 of 14
(3,672 Views)

Can you drop an actual image?  I can take a poke at it.  Will need Horizontal and vertical resolution.

0 Kudos
Message 5 of 14
(3,669 Views)

My recommendation is to stay away from signed PNGs and use only unsigned TIFFs if you would like to load the images using Python, OpenCV, ImageJ, etc

 

Let me explain. I will create simple two-pixels images, both signed and unsigned stored in PNG and TIFF:

16BitMinMaxSnippet.png

 

Now I will load all four images into Python using OpenCV and check the range:

This is my code:

 

import cv2

print('Test Signed PNG - expected -1000...1000')
image = cv2.imread('TestSigned.png', cv2.IMREAD_UNCHANGED)
min_val,max_val, min_indx, max_indx=cv2.minMaxLoc(image)
print(min_val, max_val)

print('Test Unsigined PNG - expected 0...1000')
image = cv2.imread('TestUnsigned.png', cv2.IMREAD_UNCHANGED)
min_val,max_val, min_indx, max_indx=cv2.minMaxLoc(image)
print(min_val, max_val)

print('Test Signed TIFF - expected -1000...1000')
image = cv2.imread('TestSigned.tif', cv2.IMREAD_UNCHANGED)
min_val,max_val, min_indx, max_indx=cv2.minMaxLoc(image)
print(min_val, max_val)

print('Test Unsigned TIFF - expected 0...1000')
image = cv2.imread('TestUnsigned.tif', cv2.IMREAD_UNCHANGED)
min_val,max_val, min_indx, max_indx=cv2.minMaxLoc(image)
print(min_val, max_val)

and this is result:

 

Test Signed PNG - expected -1000...1000
0.0 64031.0
Test Unsigined PNG - expected 0...1000
0.0 64062.0
Test Signed TIFF - expected -1000...1000
31768.0 33768.0
Test Unsigned TIFF - expected 0...1000
0.0 1000.0

 

Obviously the only unsigned TIFF is correct.

 

The problem could located in the encoder (NI Vision) or decoder (OpenCV) side.

Especially for PNGs I see in the hex dump that the internal NI Image Type is present:

SignedPNGDump.PNGUnSignedPNGDump.PNG

This is how NI can recognize correct type when the image loaded back using IMAQ Read File.

And "Use bit depth" flag will not help much.

 

So, the easiest way is to use unsigned TIFF when 16 bit images needs to be loaded somewhere outside NI Vision.

Message 6 of 14
(3,652 Views)
Solution
Accepted by topic author dgagnon05

Hi all, 

 

Thanks for all for the suggestions and help!

I agree that the signed images don't make too much sense but it's legacy software and results need to match up so I'm stuck using them.

Also, my application needs to work with archived data so changing the format to one that supports I16 images isn't an option. 

 

I did find a solution though, I made a simple VI that opens the image and outputs the array.

Then I made this into a dll and wrote a script in python to access the dll. 

 

If it helps anyone I've attached the dll and the python script 

 

Message 7 of 14
(3,631 Views)
Solution
Accepted by topic author dgagnon05

Finally understood everything with NI's PNG.

Its quite simple. The NI's 16 bit PNG images stored together with sBIT. It means that the pixel's values are shifted to the highest bit (this is a part of PNG Standard, but most libraries aren't aware about this).
Refer to http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.sBIT

 

In additional, the Offset for signed representation stored in first two bytes of the scAl chunk. So, all what you need - just read "RAW" png data, then perform shift according sBIT value, then subtract offset taken from scAl chunk. That is all (if you want to get things done without "extra" DLL). Attached example for both I16 and U16 images.

 

By the way - there is a bug in PNG encoder from NI. Instead of logical shifting they performing rotating (in some strange form), so the less significant bits which supposed to be zeroes - are not zeroes, as result we have these strange values mentioned above (for example, when the range 0...1000 turned to 0...64062. It should be 0...64000 instead). But this is not a big problem, because these bits goes away after shifting back.

 

Also in Manual it wrongly stated that Use Bit Depth input is applicable to signed images only. On signed images this control will cut off negative values, but on unsigned images (when used together IMAQ Image Bit Depth) this will allow to save "native" 16 bit values, so the image can be opened in third-party libraries without conversion.

Message 8 of 14
(3,584 Views)

And just one more thing - its about compression.

From NI Manual: 

Image Quality specifies the amount of compression applied to the image. Image Quality can range from 0 to 1000. The default value is 750. The higher the value, the less compression the VI applies.

 

There are no 1001 different levels. Some values will produce exactly the same output (for example 750...759, 800...809 are exactly the same). Technically IDAT chunk compression allow only few different levels of the compression, in additional we have 5 possible filters (None, Sub, Up, Average, Paeth).

Refer to chapter 5:

http://www.libpng.org/pub/png/spec/1.2/PNG-Compression.html

and chapter 6:

http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html

It means - it is not guaranteed that the lover values will perform better compression, it depends from the content of the images (for example, for 16 bit xray images mos better values when Sub filter is used, somethng like 10, 90 or 110).

Full table of the Image Quality Input used in IMAQ Write File 2 for PNG:

 

0...309 - DA - Maximum compression:
0...9, 50...59 - 0 - None
10...49 - 1 - Sub
60...69 - 4 - Paeth
70...79 - 3 - Average
80...89 - 2 - Up
90...99 - 1 - Sub
100...109 - 0 - None

110...149 - 1 - Sub
150...159, 200...209 - 0 - None
160...169 - 4 - Paeth
170...179 - 3 - Average
180...189 - 2 - Up
190...199 - 1 - Sub

210...249 - 1 - Sub
250...259, 300...309 - 0 - None
260...269 - 4 - Paeth
270...279 - 3 - Average
280...289 - 2 - Up
290...299 - 1 - Sub

 

310...509 - 9C - Default compression
310...349 - 1 - Sub
350...359, 400...409 - 0 - None
360...369 - 4 - Paeth
370...379 - 3 - Average
380...389 - 2 - Up
390...399 - 1 - Sub

410...449 - 1 - Sub
450...459, 500...509 - 0 - None
460...469 - 4 - Paeth
470...479 - 3 - Average
480...489 - 2 - Up
490...499 - 1 - Sub

 

510...709 - 5E - Fast compression
510...549 - 1 - Sub
550...559, 600...609 - 0 - None
560...569 - 4 - Paeth
570...579 - 3 - Average
580...589 - 2 - Up
590...599 - 1 - Sub

610...649 - 1 - Sub
650...659, 700...709 - 0 - None
660...669 - 4 - Paeth
670...679 - 3 - Average
680...689 - 2 - Up
690...699 - 1 - Sub

 

710...909 - 01 - Superfast compression
710...749 - 1 - Sub
750...759, 800...809 - 0 - None. DEFAULT
760...769 - 4 - Paeth
770...779 - 3 - Average
780...789 - 2 - Up
790...799 - 1 - Sub

810...849 - 1 - Sub
850...859, 900...909 - 0 - None
860...869 - 4 - Paeth
870...879 - 3 - Average
880...889 - 2 - Up
890...899 - 1 - Sub

 

910...1000 - DA - Store (no compression)
910...949 - 1 - Sub
950...959, 1000 - 0 - None
960...969 - 4 - Paeth
970...979 - 3 - Average
980...989 - 2 - Up
990...999 - 1 - Sub

 

May be will be helpful for someone...

Andrey.

 

Message 9 of 14
(3,580 Views)

Good stuff.. Just want to say:  You Rock!

Message 10 of 14
(3,564 Views)