C# - Can I use Libtiff to output Tiff encoded JPEG (In YCbCr) - c#

Hi I am quite new to Libtiff and image processing, and I have a question when I try to use Libtiff.net from Bitmiracle.
I have some OJPEG TIFF image and want to convert them into nowadays JPEG TIFF. I achieved that by converting the source into BMP and then save as TIFF (compression: JPEG; photometric: RGB), but the size of the image is quite large. So I thought if I can compress them with photometric of YCbCr, which can reduce the size a lot.
However, when I change the photometric from RGB to YCbCr, the program just don't work: the output is only 8 bytes (the input is about 400kb). When open the image as TXT, it shows:
"II* "
The code I use is:
byte[] raster1 = getImageRasterBytes(inputbmp[0], PixelFormat.Format24bppRgb);
tif1.SetField(TiffTag.IMAGEWIDTH, inputbmp[0].Width);
tif1.SetField(TiffTag.IMAGELENGTH, inputbmp[0].Height);
tif1.SetField(TiffTag.COMPRESSION, Compression.JPEG);
tif1.SetField(TiffTag.PHOTOMETRIC, Photometric.YCBCR);
tif1.SetField(TiffTag.ROWSPERSTRIP, inputbmp[0].Height);
//tif1.SetField(TiffTag.JPEGQUALITY, confidence);
tif1.SetField(TiffTag.XRESOLUTION, 200);
tif1.SetField(TiffTag.YRESOLUTION, 200);
tif1.SetField(TiffTag.BITSPERSAMPLE,8);
tif1.SetField(TiffTag.SAMPLESPERPIXEL, 3);
int stride = raster1.Length / inputbmp[0].Height;
convertSamples(raster1, inputbmp[0].Width, inputbmp[0].Height);
for (int i = 0, offset = 0; i < inputbmp[0].Height; i++)
{
tif1.WriteScanline(raster1, offset, i, 0);
offset += stride;
}
I'm not quite sure if WriteScanline can handle YCbCr output or not, and if there are other ways, It's totally cool too.
Thank you for the help!

Output images are definetely broken. It usually happens when some fatal error occurred. The library should output warnings and errors into console in such a case.
Please investigate the warnings and errors. They might give you the answer.
Based on experience, I think you might have provided incorrect value for ROWSPERSTRIP tag. The value should be a multiple of 8.

JPEG converts image into YCbCr internally already, so I don't think you will benefit from this.

Related

How to Display this image in C#

I have an image https://drive.google.com/file/d/16Xotc-2CJ6HkEJDysfKBkjClkU1OGiyQ/view?usp=sharing that is GrayScale but every library I have tried, ImageMagick, ImageSharp, System.Drawing seem to interpret it as black and white, but when you open it in ImageJ or Photoshop or Incarta or many other software you can clearly see it is grayscale.
can anyone help me find a way to display this image? here is something I've tried but i've tried almost a dozen different things
TiffEncoder encoder = new TiffEncoder();
encoder.PhotometricInterpretation = SixLabors.ImageSharp.Formats.Tiff.Constants.TiffPhotometricInterpretation.BlackIsZero;
SixLabors.ImageSharp.Image image = SixLabors.ImageSharp.Image.Load(mysteryTiff);
PixelTypeInfo pixType = image.PixelType;
// Stretches the image to fit the pictureBox.
Stream stream = new MemoryStream();
image.SaveAsTiff(stream, encoder);
stream.Position = 0;
MagickImage magickImage = new MagickImage(stream);
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.ClientSize = new System.Drawing.Size(1200, 1200);
pictureBox1.Image = magickImage.ToBitmap();
Can anyone display this image correctly. It will display correctly when uploaded to
What you have there, according to the image tag directory, is a 2024x2024 16-bpp greyscale LZW-compressed extended TIFF. It even opens in some software, which proves that it's not malformed. So far so good.
Now here's where it breaks down: 16-bpp greyscale is not supported by a lot of things. The 'why' is mildly convoluted, having to do largely with "but we all use 8 bits per channel, and so does the hardware, so why bother", but the end result isn't: if you want to use anything above 8 bits per channel, you'll either have to find something that will do the work for you or convert the data to 8-bpp at some point.
Even when the file format explicitly support 16-bpp greyscale (TIFF and PNG for instance), most libraries tend not to support either read or write in that format because it is so rarely used that they don't bother to implement it. I ended up writing my own PNG encoder for 16-bpp greyscale images (converted from 12-bpp and 16-bpp XRAY images), but the images aren't viewable in most programs that supposedly support the full PNG standard.
In this case your best option is probably going to be to write a conversion of your own for this type of file. Assuming that the same format (16-bpp, LZW-compressed) is produced by the source application every time, it shouldn't be too difficult to convert the pixel buffer to 8-bpp and save out as TIFF, PNG or whatever you like. You'll lose half of your greyscale (depth) resolution, but for display purposes they're not going to help much anyway. It only really matters when there's a good reason to retain the full range of values.

GDI+ error when loading a high resolution JPG

I met the common GDI+ error when trying to load a JPG image by C#. I am not sure if it is due to the high resolution of this JPG (46495px*4536px) because loading other low resolution JPG files works fine. The issue JPG file size is 4696KB.
Code:
var newImage = Image.FromFile("demo.jpg"); //issue jpg
It also failed when using Image.FromStream() API:
var stream = File.OpenRead("demo.jpg");
var image = Image.FromStream(stream);
Much appreciation if anyone could help explain
You need the available RAM to store the decompressed image bitmap
On a 32bit display you will require width * height * 4 + c bytes free, where c is unknown depending on the implementation of the drawing classes used.
Example
In your specific case, the calculation is as follows:
46495 * 4536 * 4 + c = 843605280 bytes + c = 805mb + c
Use the following to see how much memory is available for your bitmap.
Include a reference to the VisualBasic dll:
using Microsoft.VisualBasic.Devices;
The method is as follows:
Console.Out.Write(new ComputerInfo().AvailablePhysicalMemory + "bytes free");
...or...
Console.Out.Write((ComputerInfo().AvailablePhysicalMemory / 1048576) + "mb free");
Find c
To find c, use the method above both before AND after an image load.
By loading a number of images successfully and recording the memory used before and after the load.
Experiment by comparing the memory used before and after loading different sizes of image, taking into account the size of the bitmap and you will discover a close approximation of c.
Be aware that all image types are converted to a raw bitmap internally for viewing, regardless of whether it's stored as a .jpg, .png, .gif or whatever. So when I say Bitmp, I'm not referring to the extension .bmp. Instead, I am referring to a bitmp in a literal sense as a raw image file i.e. a map of bits.
the GDI+ will throw an "OutOfMemoryException" if it does not support the pixel format of the file.

Cannot convert YUV420 frame to Bitmap

I am trying to convert YUV420 frames to Bitmap or Image. I am reading these frames from an MP4 video in C# using the AVBlocks library. So, after creating an input and output socket using AVBlocks classes, I then pull each frame from the video with a YUV420 color format and UncompressedVideo stream type. I basically do this by calling Transcoder.Pull(int outputIndex, MediaSample outputData) and then the MediaBuffer that's part of the outputData has the data in an array of bytes. So I am trying to convert these bytes to a Bitmap or Image so that I can eventually show each frame into a PictureBox in the Winforms application.
What I've tried:
I have tried using a MemoryStream, as shown below, but I get an unhandled ArgumentException saying that the parameter is not valid. I tried using ImageConverter() as well to convert to an Image, but I get the same exception. Then, I converted the byte array from YUV to RGB format and gave the updated array as a parameter to the MemoryStream, but again no luck. I also tried changing the color format of the output socket from YUV420 to a BGR format, but it resulted in the same issue as above. The code that tries to convert to a bitmap using MemoryStream:
while (transcoder.Pull(out inputIndex, yuvFrame))
{
buffer = (MediaBuffer) yuvFrame.Buffer.Clone();
Bitmap b;
byte[] temp = new byte[buffer.DataSize];
Array.Copy(buffer.Start, buffer.DataOffset, temp, 0, buffer.DataSize);
var ms = new MemoryStream(temp);
b = new Bitmap(ms);
}
The aforementioned exception is thrown in the last line of the code. I'm not sure if it's the color format or the stream type, or something else that's causing the problem. If someone wants to see more of the code (setting up input & output sockets etc), let me know. For reference, the link to the example I've been following from AVBlocks is this and the link to MediaBuffer class is this.
The Bitmap(MemoryStream ms) constructor expects the bytes from an actual file, like a png, jpeg, bmp or gif. If I'm reading this correctly, you don't have that; you only have pure RGB triplets data. That isn't enough, because it lacks all information about the image's width, height, colour depth etc.
You will need to actually construct an image object from the RGB data. This isn't really trivial; it means you need to make a new image object with the correct dimensions and colour format, then access its backing bytes array, and write your data into it. The actual code for creating an image out of a byte array can be found in this answer.
Note that you'll have to take into account the actual stride in the resulting data you get; the amount of bytes on each line of the image. Images are saved per line, and those lines are usually padded to a multiple of 4 bytes. This obviously messes up a lot if you don't take it into account.
If your data is completely compact, then the stride to give to the BuildImage function I linked to will just be your image width multiplied by the amount of bytes per pixel (should be 3 for 24bpp RGB), but if not, you'll have to pad it to the next multiple of 4.

Decode an JFIF image file in C#

I have a JPEG image which is JFIF formatted. I want to decode it and get the dimension.
Here is my code:
while (binaryReader.ReadByte() == 0xff)
{
byte marker = binaryReader.ReadByte();
ushort chunkLength = binaryReader.ReadLittleEndianInt16();
if (marker == 0xc0)
{
binaryReader.ReadByte();
int height = binaryReader.ReadLittleEndianInt16();
int width = binaryReader.ReadLittleEndianInt16();
return new Size(width, height);
}
binaryReader.ReadBytes(chunkLength - 2);
}
Ok. This piece of code is common and you can find it all over the internet. It works fine for most of the JPEG images.
Now, this specific image which was taken by the camera - "Canon EOS 300D DIGITAL", does not support this piece of code. The marker for the dimension is 0xFFC2 instead of 0xFFC0.
My question is which one is correct? If the code is correct, then how can a Canon camera produce a non-standard image? If the Canon camera is correct, then how can we fix the code to correct find the dimension of this image?
Thanks.
FFC2 seems to be the marker for progressive images.
please see for example http://en.wikipedia.org/wiki/JPEG which explains what "progressive" format is (see "JPEG compression" section).
yes, i think you can change your if statement to check for both 0xc0 (SOF0 marker) and 0xc2 (SOF2 marker) because they seem to have similar structure (see "syntax and structure" section). see also here: http://fjcore.googlecode.com/svn/trunk/FJCore/Decoder/JpegDecoder.cs
i am not expert in JPEG formats, so you might want to check with specialised forums if you are developing a mission critical code.

Cannot open a .tif with color using .Net Image or Bitmap classes

I am attempting to open .tif files that have color in them (300 dpi, PixelFormat.Format24bppRgb) using the .Net Image and Bitmap classes. I always get an "invalid parameter" error when the tiffs have color (works fine for black and white tiffs). If anyone has source code on how to open a .tif with color in it I'd deeply appreciate it. Below is what I'm attempting to do; this also fails when calling Bitmap.FromStream:
using (FileStream fs = File.OpenRead(fileName))
{
using (Image img = Image.FromStream(fs)) {}
}
The reason why you can't open the image file is that the image maybe has compression format.
I also encounter this problem when I open a .gif image file.
I'm searching the solutions.
We can communicate each other.
showlie#163.com
thanks
I think the basic problem is that one cannot use the .Net Image/Bitmap classes to reliably open color TIFFs. Many of the various compression formats used to encode color TIFFs break the .Net Image/Bitmap class.
It seems to be a catch 22--you have to know the TIFF formatting information to know how to load it, but in .Net one needs to load the Image/Bitmap class to read TIFF formatting information.
I think the answer to my question is, "this can't be done just using the .Net framework," and that I'll need to either buy a 3rd party control or create something myself in C++.
I found the GDI(+) the limiting factor in dealing with TIFF. I had many "invalid parameter" issues on XP (in my case because of using 16bpp grayscale and tiled tiff). The same code worked flawlessly on Windows7/64bit.
For me, the free LibTIFF.NET was the solution. (StackOverflow using LibTIFF from c#) Only con compared to GDI+: you'll have to roll your own BMP (uncompressed byte array to bmp conversion). I can provide you with examples if you like.
To diagnose your TIFF file type, you could use TiffDump download link
Using like tiffdump myfile.tif will produce an output like:
Magic: 0x4949 <little-endian> Version: 0x2a
Directory 0: offset 1669857288 (0x63880008) next 0 (0)
ImageWidth (256) SHORT (3) 1<33227>
ImageLength (257) SHORT (3) 1<24986>
BitsPerSample (258) SHORT (3) 1<16>
Compression (259) SHORT (3) 1<1>
Photometric (262) SHORT (3) 1<1>
FillOrder (266) SHORT (3) 1<1>
SamplesPerPixel (277) SHORT (3) 1<1>
XResolution (282) RATIONAL (5) 1<6400>
YResolution (283) RATIONAL (5) 1<6400>
PlanarConfig (284) SHORT (3) 1<1>
ResolutionUnit (296) SHORT (3) 1<2>
TileWidth (322) SHORT (3) 1<256>
TileLength (323) SHORT (3) 1<256>
Most likely such info can get you more specifc answers on StackOverflow.

Categories

Resources