Anyone out there has code to read a 48 bit (16 bits per colour channel) tiff file?
Printable View
Anyone out there has code to read a 48 bit (16 bits per colour channel) tiff file?
As far as I understand a Tiff is not really an image format itself rather a way of containing one or more files of different formats as one file. The image contained could be a Ping, Jpeg, Bitmap or raw data (a bitmap without a header).
If this image is in Jpeg2000 format then I don't know how to extract it from the Tiff. If it's in a raw or bitmap format it should be fairly straight forward, assuming it's a RGB format rather than a CYMK (printer) format. Once the pixel data has been found it's a case of copying every other byte (the hi order byte) to a new image. A 48bit image cannot be displayed without losing information.
How huge are the files in question? I think you still have my email, if you do you could send it to me and I'll be better placed to tell you how to extract the image.
Well, I have no idea about its structure.Quote:
Originally Posted by Milk
Actually I want to separate the 3 channels, R G & B and plot one of them as grayscale.Quote:
Originally Posted by Milk
Just about 2 or 3 Mb. But I've cropped an image which is now about 100 kb to e-mail it to you.Quote:
Originally Posted by Milk
Cheers for that...
Right it's a Big Endian tiff which is a pain to read with VB which likes Little Endian numbers. The program I have written is not yet capable of reading Big Endian Tiffs but this is something I've been meaning to address for some time, this sounds like just the excuse I need. In the meantime there might be some some Tiff parser you can find at PSC or somesuch that will do the job.
The size of the file compared to the size of the image within does suggest that it's stored in a raw or Bitmap format of 6 bytes per pixel, making it easy to extract once the Tiff directories can be navigated.
The information that is needed are the image dimensions and the offset of the pixel data.
Well, I'm using a third party program, ImageJ ( http://rsbweb.nih.gov/ij/ ) which allows me to load the tiff and then save it as an array of tab separated text values, which is easy to handle with vb. But I want to skip this step and that's why I'd like to handle the tiff with vb.Quote:
Originally Posted by Milk
Image size is 181 x 106 pixels. I'm going to e-mail you the ImageJ converted file.Quote:
Originally Posted by Milk
Forgot to mention: in case it's any use to you, in the ImageJ web site the Java source code -including the tiff decoding part- is available, but I don't know Java.
***An email exchange later***
Attached is a class that can parse Tiffs, here is an example of some code to extract the byte data.The function that receives the 48bit (6 byte/pixel) byte data must choose one of the three high order bytes for the greyscale image. The High order bytes will be the odd indexes for Little-Endian data (even for Big-En...) . It is possible with DibSection Api to work with GreyScale Bitmaps, copy the right bytes to one of those and you're laughing.vb Code:
Option Explicit Private Sub Form_Load() Dim Tiff As cTiffInfo, Pix() As Byte Dim Wdth As Long, Hght As Long Dim IsBigEndian As Boolean Dim i As Long Set Tiff = New cTiffInfo With Tiff .OpenTiff App.Path & "/euro 600 dpi.tif" 'this locks the file until CloseTiff or class terminate Wdth = .ReadTag(ImageWidth) '<-- Endianess is converted for all tag data Hght = .ReadTag(ImageLength) 'TIFFs support many datatypes that VB6 does not, beware of signage. Pix = .PixelData '<-- Endianess is NOT converted for pixel data If UBound(Pix) + 1 = Wdth * Hght * 6 Then 'Is it 48-Bit per pixel, Good enough for this.... '(TIFF in theory can use a different number bits for each channel) Call My48BitImageFunction(Pix, Wdth, .IsBigEndian) Else Debug.Print "Not a 48bit image" End If End With Set Tiff = Nothing 'not required, just a good habit End Sub
I think that Java code you have converts to BigEndian, the scanner TIFFs you sent are Intel friendly.
Good Luck
This looks incredible :thumb: let me just take a deep breath and start playing around with it.
Maybe this ImageJ thing was first developed for Unix, I once wrote code for parsing files ftp'd from a Unix system and I seem to recall they were big endian.Quote:
Originally Posted by Milk
I'm really happy with your code. For now I've just managed to extract the channels,
I assume you have to switch the bytes for big endian, for example,VB Code:
Dim redch() As Long, greench() As Long, bluech() As Long Private Sub ExtractChannels(pix, redch, greench, bluech) Dim pch As Long, i As Long, j As Long pch = (1 + UBound(pix)) \ 6 - 1 ReDim redch(pch), greench(pch), bluech(pch) For i = 0 To pch j = 6 * i redch(i) = 256 * pix(j + 1) + pix(j) greench(i) = 256 * pix(j + 3) + pix(j + 2) bluech(i) = 256 * pix(j + 5) + pix(j + 4) Next End Sub
redch(i) = 256 * pix(j) + pix(j + 1)
Edit: this is only because I want to keep the full information per channel that I need for some further processing. Thereafter, when it comes to plot the image then I can keep only the high order byte, odd for little endian as you've pointed out.
...and I've created the bitmap from the high order bytes.
and now it can be called for plotting from within your code above:VB Code:
Private Sub Create2DBitmap(pix, wd, ht, bm) Dim i As Long, j As Long Dim row As Integer, col As Integer Dim nPx As Long ReDim bm(wd - 1, ht - 1) 'The 2D bitmap to be filled nPx = (1 + UBound(pix)) \ 6 - 1 'Total number of pixels For i = 0 To nPx j = 6 * i + 1 row = (nPx - i) \ wd 'The rows must start at the 'bottom or else the image will be inverted col = wd - 1 - (nPx - i - row * wd) bm(col, row) = RGB(pix(j + 4), pix(j + 2), pix(j)) Next End Sub
where DrawData is the function you sent me some time ago based on SetDIBitsToDevice.VB Code:
'... Dim mybm() As Long '... If UBound(Pix) + 1 = Wdth * Hght * 6 Call Create2DBitmap(pix, Wdth, Hght, mybm) Picture1.Width = Wdth + 2 * bWdt 'Account for 'the border width of the picturebox Picture1.Height = Hght + 2 * bWdt Call DrawData(Picture1.hdc, mybm) Else Debug.Print "Not a 48bit image" End If '...
I thought it might be useful to transcribe here your DrawData function:
VB Code:
'(In a module) Option Explicit Private Type RGBQUAD blue As Byte green As Byte red As Byte alpha As Byte End Type Private Type BITMAPINFOHEADER bmSize As Long bmWidth As Long bmHeight As Long bmPlanes As Integer bmBitCount As Integer bmCompression As Long bmSizeImage As Long bmXPelsPerMeter As Long bmYPelsPerMeter As Long bmClrUsed As Long bmClrImportant As Long End Type Private Type BITMAPINFO bmHeader As BITMAPINFOHEADER bmColors(0 To 255) As RGBQUAD End Type Private Declare Function SetDIBitsToDevice Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, _ ByVal y As Long, ByVal dx As Long, ByVal dy As Long, ByVal SrcX As Long, ByVal SrcY As Long, _ ByVal Scan As Long, ByVal NumScans As Long, Bits As Any, BitsInfo As BITMAPINFO, _ ByVal wUsage As Long) As Long Public Declare Function StretchBlt Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, ByVal dwRop As Long) As Long Public Declare Function SetStretchBltMode Lib "gdi32" (ByVal hdc As Long, ByVal hStretchMode As Long) As Long Public Const STRETCHMODE = vbPaletteModeNone Public Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal dwRop As Long) As Long Public Const SRCCOPY = &HCC0020 '... '... Public Sub DrawData(handledc As Long, lngImageData2D() As Long) Dim bmi As BITMAPINFO With bmi.bmHeader .bmBitCount = 32 .bmPlanes = 1 .bmSize = Len(bmi.bmHeader) .bmWidth = UBound(lngImageData2D, 1) + 1 .bmHeight = UBound(lngImageData2D, 2) + 1 SetDIBitsToDevice handledc, 0, 0, .bmWidth, .bmHeight, 0, 0, 0, .bmHeight, lngImageData2D(0, 0), bmi, 0 End With End Sub