How can I make a new class to check for images types?

Viewed 84

I created a new class :

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Images
{
    class ImageTypeCheck
    {
        public static byte[] ImageToByteArray(System.Drawing.Image imageIn)
        {
            using (var ms = new MemoryStream())
            {
                imageIn.Save(ms, imageIn.RawFormat);
                return ms.ToArray();
            }
        }

        public enum ImageFormats
        {
            bmp,
            jpeg,
            gif,
            tiff,
            png,
            unknown
        }

        public static ImageFormats GetImageFormat(byte[] bytes)
        {
            var bmp = Encoding.ASCII.GetBytes("BM");     // BMP
            var gif = Encoding.ASCII.GetBytes("GIF");    // GIF
            var png = new byte[] { 137, 80, 78, 71 };    // PNG
            var tiff = new byte[] { 73, 73, 42 };         // TIFF
            var tiff2 = new byte[] { 77, 77, 42 };         // TIFF
            var jpeg = new byte[] { 255, 216, 255, 224 }; // jpeg
            var jpeg2 = new byte[] { 255, 216, 255, 225 }; // jpeg canon

            if (bmp.SequenceEqual(bytes.Take(bmp.Length)))
                return ImageFormats.bmp;

            if (gif.SequenceEqual(bytes.Take(gif.Length)))
                return ImageFormats.gif;

            if (png.SequenceEqual(bytes.Take(png.Length)))
                return ImageFormats.png;

            if (tiff.SequenceEqual(bytes.Take(tiff.Length)))
                return ImageFormats.tiff;

            if (tiff2.SequenceEqual(bytes.Take(tiff2.Length)))
                return ImageFormats.tiff;

            if (jpeg.SequenceEqual(bytes.Take(jpeg.Length)))
                return ImageFormats.jpeg;

            if (jpeg2.SequenceEqual(bytes.Take(jpeg2.Length)))
                return ImageFormats.jpeg;

            return ImageFormats.unknown;
        }
    }
}

Then in form1 when I loop over images I want to compare and check what type is each image :

foreach (string file in images)
            {
                using (var img = Image.FromFile(file))
                {

                    var imgType = ImageTypeCheck.GetImageFormat(ImageTypeCheck.ImageToByteArray(img));
                    
                    if (imgType != ImageFormat.Gif)
                    {
                        var destinationImagePath =
                            Path.ChangeExtension(@"d:\images\" + counter.ToString(), "gif");

                        img.Save(destinationImagePath, ImageFormat.Gif);
                    }
                }

I'm getting error on the line :

if (imgType != ImageFormat.Gif)

Operator '!=' cannot be applied to operands of type 'ImageTypeCheck.ImageFormats' and 'ImageFormat'

imgType does not contains any formats from the class. I want to make that only if the image type is not real gif then convert it to gif.

Before I removed all the checking part to a new class it was all in form1 both the ImageToByteArray and the GetImageFormat and the enum. And only the GetImageFormat method was public static.

And then I used it like this without any errors :

foreach (string file in images)
            {
                using (var img = Image.FromFile(file))
                {

                    var imgType = GetImageFormat(ImageToByteArray(img));

                    if (imgType != ImageFormat.gif)
                    {
                        var destinationImagePath =
                            Path.ChangeExtension(@"d:\images\" + counter.ToString(), "gif");

                        img.Save(destinationImagePath, System.Drawing.Imaging.ImageFormat.Gif);
                    }
                }

but once I moved the two methods to the new class it's not working and give the error.

1 Answers

I am not sure why you are doing all this code when you can just check RawFormat

Gets the file format of this Image.

var image = Image.FromFile(@"D:\200.gif");
var isGif = image.RawFormat.Equals(ImageFormat.Gif);

However, if you really needed to do something like this, here is an extremely efficient way of achieving it. Note, this is actually pulled from one of my runtime libraries to check for binary sequences and modified for your example. So take it with a grain of salt.

public static class ImageTypeCheck
{
   public enum ImageFormats
   {
      bmp,
      jpeg,
      gif,
      tiff,
      png,
      unknown
   }

   private static readonly ImmutableArray<(Memory<byte> bytes, ImageFormats format)> Headers;

   private static readonly int MaxLength;
   static ImageTypeCheck()
   {

      Headers = ImmutableArray.Create(
         new (Memory<byte> bytes, ImageFormats format)[]
         {
            (Encoding.ASCII.GetBytes("BM"), ImageFormats.bmp),
            (Encoding.ASCII.GetBytes("GIF"), ImageFormats.gif),
            (new byte[] {137, 80, 78, 71}, ImageFormats.png)
         });
      MaxLength = Headers.Max(x => x.bytes.Length);
   }

   public static ImageFormats GetImageFormat(Image imageIn)
   {
      var buffer = new byte[MaxLength];
      using var ms = new MemoryStream();
      imageIn.Save(ms, imageIn.RawFormat);
      var span = buffer.AsSpan();
      ms.Position = 0;
      ms.Read(span);
      return GetImageFormat(span);
   }

   public static ImageFormats GetImageFormat(Stream stream)
   {
      var buffer = new byte[MaxLength];
      var span = buffer.AsSpan();
      stream.Read(span);
      return GetImageFormat(span);
   }

   public static ImageFormats GetImageFormat(ReadOnlySpan<byte> source)
   {
      foreach (var (bytes, format) in Headers)
         if (bytes.Span.SequenceEqual(source.Slice(0,bytes.Length)))
            return format;
      return ImageFormats.unknown;
   }
}

To prove that it will be faster then just sequence equals on an array, but also primarily just to use the new table feature of stackoverflow, I have a bunch of benchmarks to show the performance difference between a bunch of ways and how they allocate (if that is important to someone). Note that SequenceEqual on BitWiseEquatable span is more efficient and heavily optimised.

Environment

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.630 (2004/?/20H1)
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=5.0.101
  [Host]        : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT  [AttachedDebugger]
  .NET Core 5.0 : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT

Job=.NET Core 5.0  Runtime=.NET Core 5.0

Results

Method N Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated
Span 10 16.81 ns 0.312 ns 0.372 ns 0.0095 - - 40 B
SpanPool 10 35.84 ns 0.734 ns 1.005 ns - - - -
MemoryPool 10 32.08 ns 0.594 ns 0.793 ns - - - -
Memory 10 17.08 ns 0.372 ns 0.522 ns 0.0095 - - 40 B
Bytes 10 130.72 ns 2.646 ns 3.878 ns 0.0095 - - 40 B
BytesPool 10 54.82 ns 0.805 ns 0.753 ns - - - -
Span 100 26.53 ns 0.556 ns 1.085 ns 0.0306 - - 128 B
SpanPool 100 29.92 ns 0.523 ns 0.464 ns - - - -
MemoryPool 100 33.01 ns 0.684 ns 0.703 ns - - - -
Memory 100 26.84 ns 0.566 ns 1.207 ns 0.0306 - - 128 B
Bytes 100 736.24 ns 14.625 ns 14.364 ns 0.0305 - - 128 B
BytesPool 100 50.91 ns 0.754 ns 0.706 ns - - - -
Span 1000 116.17 ns 2.346 ns 3.050 ns 0.2446 - - 1024 B
SpanPool 1000 31.16 ns 0.627 ns 0.616 ns - - - -
MemoryPool 1000 32.59 ns 0.671 ns 1.045 ns - - - -
Memory 1000 121.22 ns 2.457 ns 4.963 ns 0.2446 - - 1024 B
Bytes 1000 6,489.62 ns 127.604 ns 248.883 ns 0.2441 - - 1024 B
BytesPool 1000 76.19 ns 0.889 ns 0.742 ns - - - -

Test Code

[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
public class DumbTest
{
   [Params(10,100,1000)]
   public int N;

   private byte[] _bytes;
   private Memory<byte> _memory;
   private MemoryStream _ms;

   [GlobalSetup]
   public void GlobalSetup()
   {
      var r = new Random(32);
      _bytes = new byte[N];
      r.NextBytes(_bytes);
      _ms = new MemoryStream(_bytes);

       _memory = _bytes.AsMemory();

   }

   [Benchmark]
   public void Span()
   {
      _ms.Position = 0;
      var buffer = new byte[N];
      var span = buffer.AsSpan();
      _ms.Read(span);
      var asd =span.SequenceEqual(_bytes);
   }
   [Benchmark]
   public void SpanPool()
   {
      _ms.Position = 0;
      var buffer = arrayPool.Rent(N);
      try
      {
         var span = buffer.AsSpan(N);
         _ms.Read(span);
         var asd =span.SequenceEqual(_bytes);
      }
      finally
      {
         arrayPool.Return(buffer);
      }

   }
   
   [Benchmark]
   public void MemoryPool()
   {
      _ms.Position = 0;
      var buffer = arrayPool.Rent(N);
      try
      {
         var span = buffer.AsSpan(N);
         _ms.Read(span);
         var asd =span.SequenceEqual(_memory.Span);
      }
      finally
      {
         arrayPool.Return(buffer);
      }
   }

   [Benchmark]
   public void Memory()
   {
      _ms.Position = 0;
      var buffer = new byte[N];
      var span = buffer.AsSpan();
      _ms.Read(span);
      var asd =span.SequenceEqual(_memory.Span);
   }
   [Benchmark]
   public void Bytes()
   {
      _ms.Position = 0;
      var buffer = new byte[N];
      _ms.Read(buffer, 0, buffer.Length);
      var asd =buffer.SequenceEqual(_bytes);
   }

   private static readonly ArrayPool<byte> arrayPool = ArrayPool<byte>.Shared;
   [Benchmark]
   public void BytesPool()
   {
      _ms.Position = 0;
      var buffer = arrayPool.Rent(N);
      try
      {
         _ms.Read(buffer, 0, N);
         var asd =buffer.SequenceEqual(_bytes);
      }
      finally
      {
         arrayPool.Return(buffer);
      }

   }

}
Related