ImageSharp icon indicating copy to clipboard operation
ImageSharp copied to clipboard

Load Image from an array of Bytes invert color

Open dfornero opened this issue 2 years ago • 16 comments

Prerequisites

  • [X] I have written a descriptive issue title
  • [X] I have verified that I am running the latest version of ImageSharp
  • [X] I have verified if the problem exist in both DEBUG and RELEASE mode
  • [X] I have searched open and closed issues to ensure it has not already been reported

ImageSharp version

2.1.1

Other ImageSharp packages and versions

2.1.1

Environment (Operating system, version and so on)

Windows 10

.NET Framework version

Net Core 3.1

Description

Hello again; I can load an image (tiff format with differents sizes) from an array of bytes using "FrameDecodingMode.First"; so far so god. But randomly, the library load the image inverting colors (black & white, insted white & black). the original image is white with black lines; sometimes, it load the image as black with white lines. Any help? Thanks a lot...

Steps to Reproduce

byte[] tiffImage = GetImageFromBytes(); // this load the array with one image from a database TiffDecoder decoder = new TiffDecoder(); decoder.DecodingMode = FrameDecodingMode.First; using (Image img = Image.load(tiffImage, decoder); { }

Images

No response

dfornero avatar May 11 '22 19:05 dfornero

You’re supposed to provide means to reproduce issues. You have not done that.

If you are unable to do so we will have to close this issue.

JimBobSquarePants avatar May 11 '22 22:05 JimBobSquarePants

Sorry, my mistake; I send you two images; they are only examples. I can not send you the real images, but i think they represent the situation.

Imagen2 Imagen1

dfornero avatar May 12 '22 00:05 dfornero

@dfornero the images you have provided are jpg images and not tiff images.

brianpopow avatar May 12 '22 07:05 brianpopow

Hi; Unfortunely I can not send you the original images; they are confidential information. I send you two jpg just to ilustrate what happen in this case.

dfornero avatar May 12 '22 11:05 dfornero

Then I’m afraid the only way this potential issue could be fixed is by someone from your side submitting a PR with the fix. Without a means to replicate we have nothing to debug.

JimBobSquarePants avatar May 12 '22 11:05 JimBobSquarePants

Ok, thanks any way.

dfornero avatar May 12 '22 11:05 dfornero

@brianpopow wait, TIFF images can be encoded with jpeg YccK color, right?

Usually such 'inverted' images could be generated via invalid rgb -> ycck conversion formula:

// rgb -> cmyk
CmykScalar.ConvertFromRgb(in values, maxValue, rLane, gLane, bLane);

// cmyk -> ycck
Span<float> c = values.Component0;
Span<float> m = values.Component1;
Span<float> y = values.Component2;

for (int i = 0; i < y.Length; i++)
{
    // color inversion here
    // sometimes people on the internet miss it as YccK is super rare and poorly documented
    // leading to negative images
    float r = maxValue - c[i];
    float g = maxValue - m[i];
    float b = maxValue - y[i];

    // k value is passed untouched from rgb -> cmyk conversion
    c[i] = (0.299f * r) + (0.587f * g) + (0.114f * b);
    m[i] = halfValue - (0.168736f * r) - (0.331264f * g) + (0.5f * b);
    y[i] = halfValue + (0.5f * r) - (0.418688f * g) - (0.081312f * b);
}

We also don't write APP14 marker for CMYK images which can be misinterpreted by some image showing software. CMYK/YccK is the only part in the jpeg format which can cause this (is given images actually used jpeg compression).

br3aker avatar May 24 '22 13:05 br3aker

@brianpopow wait, TIFF images can be encoded with jpeg YccK color, right?

@br3aker Yes that's correct, but it's unclear if the tiff image here actually uses Jpeg compression. Since it is a black and white image, it is very likely using one of the compression schema's for black and whit (fax3/fax4 maybe). My bet is, that there is something going wrong here with the PhotometricInterpretation (its tells how the pixel data is to be interpreted) during decoding the image. This can be BlackIsZero or WhiteIsZero.

@dfornero maybe you could provide more meta data about the image in question? A useful tool for that is tiffinfo, which comes with libtiff: http://www.libtiff.org/tools.html Just use tiffinfo image.tiff

Or imagemagick:

magick identify -verbose image.tiff

brianpopow avatar May 24 '22 13:05 brianpopow

SixLabors ImageSharp 2.1.2 and .Net Core 6.0.

Same issue here working with images from a repository. They have confidential information so I can't attach the originals as in the OP's case. I'll see if I can recreate a multi-page tiff image with the same issues but I'm not sure how to do that at this point.

I can see that the existing images in my case have a 2 color indexed palette (black/white), CCITT T6 compression, while the ImageSharp tiff metadata shows null for all of the colorspaces when I load one of the images. The OPs image looks like greyscale though.

SaveAsTiff with encoder options doesn't change the results even with different PhotometricInterpretation options. So, do we have to use a pixel by pixel color space conversion to invert the black/white on our images? I don't see any automatic whole-image colorspace conversions to try unless I missed that.

CREFaulk avatar Jun 10 '22 17:06 CREFaulk

@CREFaulk without the image, it's nearly impossible to tell what's wrong (I guess something goes wrong while decoding).

What happens, when you convert the image to a png with imagemagick?

magick convert image.tiff image.png

Does the resulting png look as you expect? Is it different from what ImageSharp produces when you save it as a PNG?

brianpopow avatar Jun 10 '22 17:06 brianpopow

Here is the image info from imagemagick. https://pastebin.com/eaB7puQ6

tiff:photometric: min-is-black

If I convert to a png it looks as it should although obviously as separate images. I'll see if I can create a test image in our repository that can reproduce the issue.

CREFaulk avatar Jun 10 '22 17:06 CREFaulk

Thanks for the image info. Nothing too fancy there. Endianess is lsb, tiff:photometric: min-is-black and compression is group4. We have such test images in our unit tests working.

If I convert to a png it looks as it should although obviously as separate images. I'll see if I can create a test image in our repository that can reproduce the issue.

How did you convert it? Does converting with imagemagick work as expected or converting with ImageSharp to PNG also works as expected, but not when you encode it as Tiff?

brianpopow avatar Jun 10 '22 17:06 brianpopow

I used the imagemagick command you suggested to convert the original tiff to png file(s) and they looked like they should.

In c#, I tried converting the tiff pages to tiff, png, and jpg with the same inverted black/white results. I'm loading the pages into a pdf. The pages are right but just with the black/white reversed.

Just changing the encoding options in the saveas statement doesn't seem to have an effect on what the result looks like. That doesn't convert anything. I'd need to convert the pixels before saving to do that, right?

My wip code is in the next message. Apologies to OP for hijacking the thread but hopefully this is the same issue since the results are identical.

CREFaulk avatar Jun 10 '22 18:06 CREFaulk

public byte[] PdfFromImage( string fileName, byte[] documentBytes )
{
	byte[] byteResult;

	using ( var ms = new MemoryStream( documentBytes ) )
	{
		var document = new Aspose.Pdf.Document();
		ms.Position = 0;
		IImageFormat format;
		var imageHeight = 0;
		var imageWidth = 0;
		var bytes = ms.ToArray();
		using ( SixLabors.ImageSharp.Image bitmap = SixLabors.ImageSharp.Image.Load( ms, out format ) )
		{
			for ( var i = 0; i < bitmap.Frames.Count; i++ )
			{
				var frame = bitmap.Frames.CloneFrame( i );
				// If bitmapStream is disposed then pdf will not save with "Cannot access a closed Stream."
				// If declared separately then every page is the first page
				var bitmapStream = new MemoryStream();
				frame.SaveAsPng( bitmapStream );
				imageHeight = frame.Height;
				imageWidth = frame.Width;

				var newPage = document.Pages.Add();
				Aspose.Pdf.Image img = new Aspose.Pdf.Image();
				img.ImageStream = bitmapStream;
				img.HorizontalAlignment = HorizontalAlignment.Center;
				img.VerticalAlignment = VerticalAlignment.Center;
				newPage.Paragraphs.Add( img );
				newPage.Background = Aspose.Pdf.Color.White;
				newPage.PageInfo.IsLandscape = imageWidth > imageHeight;
				newPage.PageInfo.Height = newPage.PageInfo.IsLandscape ? imageWidth : imageHeight;
				newPage.PageInfo.Width = newPage.PageInfo.IsLandscape ? imageHeight : imageWidth;

				if ( newPage.PageInfo.Height < PORTRAIT_PAGE_HEIGHT )
				{
					newPage.PageInfo.Height = PORTRAIT_PAGE_HEIGHT;
				}
				if ( newPage.PageInfo.Width < PORTRAIT_PAGE_WIDTH )
				{
					newPage.PageInfo.Width = PORTRAIT_PAGE_WIDTH;
				}
				newPage.PageInfo.Margin.Bottom = 1;
				newPage.PageInfo.Margin.Top = 1;
				newPage.PageInfo.Margin.Left = 1;
				newPage.PageInfo.Margin.Right = 1;
			}
			using ( var pdfStream = new MemoryStream() )
			{
				document.Save( pdfStream, SaveFormat.Pdf );
				byteResult = pdfStream.ToArray();
			}              
		}
		ms.Close();
	}
	return byteResult;
}

CREFaulk avatar Jun 10 '22 18:06 CREFaulk

In c#, I tried converting the tiff pages to tiff, png, and jpg with the same inverted black/white results. I'm loading the pages into a pdf. The pages are right but just with the black/white reversed.

That means the decoder could be the problem. Again without a test image, I cannot do much.

My wip code is in the next message. Apologies to OP for hijacking the thread but hopefully this is the same issue since the results are identical.

Most of the code has nothing to do with ImageSharp, try simplify the example.

brianpopow avatar Jun 10 '22 20:06 brianpopow

Well, I guess I could have tried the obvious...

if(format.Name.Equals("TIFF"))
{
    frame.Mutate( x => x.Invert() );
}

I'm getting images from a third party repository and in my case they have social security numbers and whatnot in them so creating a test image that matches the original might be tricky. Pretty sure the issue is with the original PhotoMetric setting.

I'll see if I can reproduce with an image I create manually and write a unit test but the above will work for now as long as they are all reversed 2-color images. :|

Standard system.drawing.common code didn't have the reversed appearance issue but only worked in Windows and not at all in the current non-windows environment I deploy to even with the System.Drawing.EnableUnixSupport template option.

Edit: TiffFrameMetadata info = bitmap.Frames.RootFrame.Metadata.GetTiffMetadata();

-		info	{SixLabors.ImageSharp.Formats.Tiff.TiffFrameMetadata}	SixLabors.ImageSharp.Formats.Tiff.TiffFrameMetadata
		BitsPerPixel	Bit1	SixLabors.ImageSharp.Formats.Tiff.TiffBitsPerPixel?
-		BitsPerSample	{TiffBitsPerSample(1, 0, 0)}	SixLabors.ImageSharp.Formats.Tiff.TiffBitsPerSample?
		Channel0	1	ushort
		Channel1	0	ushort
		Channel2	0	ushort
		Channel3	0	ushort
		Channels	1	byte
		Compression	CcittGroup4Fax	SixLabors.ImageSharp.Formats.Tiff.Constants.TiffCompression?
		PhotometricInterpretation	BlackIsZero	SixLabors.ImageSharp.Formats.Tiff.Constants.TiffPhotometricInterpretation?
		Predictor	null	SixLabors.ImageSharp.Formats.Tiff.Constants.TiffPredictor?

CREFaulk avatar Jun 10 '22 22:06 CREFaulk

Closing this now, because without a test image this can not be reproduced. @CREFaulk feel free to open/comment here again, if you manage to get a image with that issue (which can be shared).

brianpopow avatar Aug 24 '22 18:08 brianpopow