0

Let's say I want a method that does some manipulation on an image by messing with its pixel values and then returns an image which is otherwise the "same" (equal PixelFormat, RawFormat, VerticalResolution, etc.).

public static Image DoSomeManipulation(this Image source)
{
    // ... 
}

I've been finding this remarkably difficult to do because to manipulate its pixels I want to use Bitmap and I can't find a way to create a Bitmap which has the same ImageFormat.

I've tried:

var target = new Bitmap(source);

and

var temp = new Bitmap(source)
var target = temp.Clone(new Rectangle(0, 0, temp.Width, temp.Height), source.PixelFormat);

and

var temp = new Bitmap(source)
var target = temp.Clone(new Rectangle(0, 0, temp.Width, temp.Height), temp.PixelFormat);

and while each of them works some of the time for my test on my 1,000 random files (PNGs, JPGs, etc.), all of them fail

Assert.AreEqual(target.ImageFormat, source.ImageFormat);

some of the time and I can't figure out why that is.

Is there any solution that doesn't involve one of

  • Serializing and deserializing source (Yes, I've seen this)
  • If source is a local file, creating copy in Temp storage or something like that

??

  • The `ImageFormat` class refers to image file formats on disk (or rather, when serialized). It does not refer to GDI's internal raster representation (a DIB or DDB). For example, you cannot directly edit JPEG in-memory in `System.Drawing` (because a JPEG file doesn't have "pixels") - that's why you have to load it into memory as a raster bitmap first. It sounds like you want to save an in-memory raster as a DIB using the `image/bmp` format that has the same pixel format as a source image? – Dai Nov 14 '19 at 04:24
  • @Dai Unfortunately I'm new too image processing and don't quite speak the language of "GDI", "raster", etc. I'll need an "explain to me like I'm 5 years old" explanatation. :) Basically, I could use an example where I do `Image.Load(sourceImagePath)`, change 1 pixel, then save back to same path (in its "original format" with same resolution and everything, if that concept even makes sense). – user11389375 Nov 14 '19 at 05:10
  • Gotcha. Well, in many cases that might not be possible depending on the change you made (e.g. loading a 256-color GIF image into a `Bitmap` then using a color that isnt in the GIF's original palette - so it's impossible to save it back to a 256-color GIF because now there's 257 colors). What are the file-types you're trying to load? (Both the container format and their internal formats, e.g. 24-bit PNG vs 256-color PNG, GIF, JFIF/JPEG using what quality settings? Etc) – Dai Nov 14 '19 at 05:13
  • If you're loading Windows `*.bmp` files (the kind you make in MSPaint) then that means you're working with raster images which is much simpler than working with compressed formats or non-raster formats - but they can have different pixel formats (e.g. 32bpp, 24bpp, 8bpp). Do you have access to Photoshop or any other imaging tool that will show you detailed information about each file? – Dai Nov 14 '19 at 05:14
  • "all of them fail some of the time and I can't figure out why that is." - So, try to put that in an "if" with a breakpoint in it and see what's actually changed? Anyway, I really think you're worrying over nothing. Clone is a rather specialised operation which you normally don't need, and `ImageFormat` has no real effect once you write it back as a specific format anyway. – Nyerguds Apr 07 '20 at 11:12

1 Answers1

0

Regarding your question to compare the images: there might be differences in images which are

  • visible on binary comparison
  • but invisible for the eye

caused especially by

  • file encoding (e.g. JPG looses some minor details for optimal compression results, but causes some pixels to change)

Instead of comparing images with the regular unit test method Assert.AreEqual you might want to consider an image comparison tool with support for a threshold to ignore minor differences caused by the compression algorithm, e.g.

Comparing example in unit test

private void AssertImagesAreEqual(System.Drawing.Image expected, System.Drawing.Image current)
{
    var DiffPercentage = global::ImageComparison.ExtensionMethods.PercentageDifference(image1, image2, 4);
    var MaxDiff = 0;
    Assert.LessOrEqual(MaxDiff, DiffPercentage, "Image difference > " + MaxDiff + " found: " + DiffPercentage);
}
Jochen
  • 380
  • 1
  • 3
  • 9