如何确定图像在C#或VB.NET中是否为灰度? [英] How to determine if an Image is Grayscale in C# or VB.NET?

查看:88
本文介绍了如何确定图像在C#或VB.NET中是否为灰度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,请注意问题不起作用对于所有灰度图像,还请注意,这另一个问题根本没有解释如何确定图像是否为灰度图像,但是无论如何它都不符合我的需求,因为它似乎仅涵盖JPEG和TIFF图像,并假设它们将具有EXIF元数据,并且其中的必填字段。 (我不明白为什么人们认为我链接的第一个问题也是我链接的第二个问题的重复 ...)

First of all, please note that the answer given in this question will not work for all the grayscale images, and also note that the accepted answer in this other question does not explain at all how to determine if an image is grayscale, but anyways it does not fit my needs because it seems to only cover JPEG and TIFF images and assuming they will have EXIF metadata and the required fields inside it. (I can't understand why people determined that the first question I linked is a "duplicated" of that second question I linked too...)

最后,<一个href = https://stackoverflow.com/a/36374901/1248295>最后一个接受的答案缺少有效的演示代码示例,但无论如何都无济于事,因为作者指的是速度慢且使用 Bitmap.GetPixel()函数不推荐使用的方法,但是对于更高版本,我们应该使用 Bitmap.LockBits()函数

And finally, this last accepted answer lacks of a working and demonstrative code example, but anyways that would not help because the author refers to the slow and deprecated methodology using Bitmap.GetPixel() function, but we should use Bitmap.LockBits() function instead for the higher performance benefit.

我有一些GIF,我需要确定它们是否为灰度图像的JPG,BMP和PNG图像。对于GIF文件,我只关心分析第一帧。

I have some GIF, JPG, BMP and PNG images for which I require to determine whether they are Grayscale images or they are not. For the GIF files I only care about analyzing the first frame.

我对图像的数据结构,像素颜色位以及那些东西没有太多的经验/了解。了解最基本的知识。因此,如果我缺少重要信息,而我应该提供将要测试的图像的任何类型的信息,请问我,但是无论如何我都想为所有图像创建通用解决方案,嗯,不是全部,但至少是以下格式:BMP,JPG,GIF和PNG。

I'm not much experienced/aware about image's data structures, pixel color bits and those things, I only know the very basics. So, if I'm missing important info and I should give any kind of info of the images I will test, then just ask me, but anyways take into account that I would like to create a generic soluton for "all" kind of images, well, not all, but at least these formats: BMP, JPG, GIF and PNG.

在提到的这些图像格式中,我最优先考虑的是GIF图像,我的意思是,如果能够确定GIF图像是否为灰度的方法与分析其他类型图像的方法不同,那么我将接受仅涵盖GIF图像像素处理的答案。

Of those image formats mentioned, my highest priority are the GIF images, with this I will mean that if the methodology to be able determine whether a GIF image is Grayscale can't be the same methodology to analyze other kind of images, then I will accept an answer that covers only GIF image pixel treatment.

我认为我的需求很明确:如何确定是否图像是灰度图像吗?。

I think my needs are clear: how can I determine whether an image is Grayscale?.

如果根本不清楚,请避免让我浪费您的时间:

In case of it was not clear at all, to avoid that I could make that you could waste your time:


  • 该解决方案必须至少对GIF图像有效。 (请记住,我只关心GIF中的第一帧),但是如果提供的解决方案也可以用于BMP,JPG和PNG,那么它当然总是更好。

  • The solution must work at least for GIF images. (remember, I only care about the first frame in the GIF), but if the provided solution can work too for BMP, JPG and PNG then of course its always better.

解决方案必须关心 PixelFormat.Format32bppRgb 灰度图像。

The solution must care about PixelFormat.Format32bppRgb grayscale images.

解决方案不得使用 Bitmap.GetPixel()函数,它必须改用 Bitmap.LockBits()

The solution must not use Bitmap.GetPixel() function, it must use Bitmap.LockBits() instead.

我不是要解释,也不是要伪造代码,也不是有关图像结构/格式/像素等文档的外部链接,我要的是有效的代码示例(如果作者涵盖图像结构/像素技术,除了提供代码外,还提供必要的说明。)

I'm not asking for explanations, pseudo-codes neither external links to documentation about image structures/formats/pixels etc, I'm asking for a working code example (of course always better if the author covers the image structures/pixels technicisms to provide essential explanations in addition to the code).

在C#或VB.NET中,选择无关紧要。

In C# or VB.NET, the choice does not matter.

到目前为止,这是我试图做的。我一直在试图了解确定图像是否为灰度图像的要点,也不确定我使用 bytesPerPixel 变量的条件是否合适,以及是否我的RGB值分配是正确的,因为从一开始我就说我不是图像处理专家,所以我可能会错过重要的事情...

This is what I tried to do so far. I'm stuck trying to understand the point to determine whether a image is Grayscale or not, also I'm not sure whether my conditions with the bytesPerPixel variable are proper and also whether my RGB value assignations are correct since as I said from the start I'm not a image-processing expert so I could have missed important things...

VB.NET

Public Shared Function IsImageGrayScale(ByVal img As Image) As Boolean

    Select Case img.PixelFormat

        Case PixelFormat.Format16bppGrayScale
            Return True

        Case Else
            Dim pixelCount As Integer = (img.Width * img.Height)
            Dim bytesPerPixel As Integer = (Image.GetPixelFormatSize(img.PixelFormat) \ 8)

            If (bytesPerPixel <> 3) AndAlso (bytesPerPixel <> 4) Then
                Throw New NotImplementedException(message:="Only pixel formats that has 3 or 4 bytes-per-pixel are supported.")

            Else
                Dim result As Boolean

                ' Lock the bitmap's bits.
                Dim bmp As Bitmap = DirectCast(img, Bitmap)
                Dim rect As New Rectangle(Point.Empty, bmp.Size)
                Dim data As BitmapData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat)

                ' Get the address of the first line.
                Dim ptr As IntPtr = data.Scan0

                ' Declare an array to hold the bytes of the bitmap. 
                Dim numBytes As Integer = (data.Stride * bmp.Height)
                Dim rgbValues As Byte() = New Byte(numBytes - 1) {}

                ' Copy the RGB values into the array.
                Marshal.Copy(ptr, rgbValues, 0, numBytes)

                ' Unlock the bitmap's bits.
                bmp.UnlockBits(data)

                ' Iterate the pixels.
                For i As Integer = 0 To (rgbValues.Length - bytesPerPixel) Step bytesPerPixel

                    Dim c As Color =
                        Color.FromArgb(red:=rgbValues(i + 2),
                                       green:=rgbValues(i + 1),
                                       blue:=rgbValues(i))

                    ' I don't know what kind of comparison I need to do with the pixels, 
                    ' so I don't know how to proceed here to determine whether the image is or is not grayscale.
                    ' ...

                Next i

                Return result
            End If

    End Select

End Function

C#(代码转换,未经测试)

C# (code conversion, untested)

public static bool IsImageGrayScale(Image img) {

    switch (img.PixelFormat) {

        case PixelFormat.Format16bppGrayScale:
            return true;

        default:
            int pixelCount = (img.Width * img.Height);
            int bytesPerPixel = (Image.GetPixelFormatSize(img.PixelFormat) / 8);

            if ((bytesPerPixel != 3) && (bytesPerPixel != 4)) {
                throw new NotImplementedException(message: "Only pixel formats that has 3 or 4 bytes-per-pixel are supported.");

            } else {
                bool result = false;

                // Lock the bitmap's bits.
                Bitmap bmp = (Bitmap)img;
                Rectangle rect = new Rectangle(Point.Empty, bmp.Size);
                BitmapData data = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);

                // Get the address of the first line.
                IntPtr ptr = data.Scan0;

                // Declare an array to hold the bytes of the bitmap. 
                int numBytes = (data.Stride * bmp.Height);
                byte[] rgbValues = new byte[numBytes];

                // Copy the RGB values into the array.
                Marshal.Copy(ptr, rgbValues, 0, numBytes);

                // Unlock the bitmap's bits.
                bmp.UnlockBits(data);

                // Iterate the pixels.
                for (int i = 0; i <= rgbValues.Length - bytesPerPixel; i += bytesPerPixel) {

                    Color c = Color.FromArgb(red: rgbValues[i + 2], 
                                             green: rgbValues[i + 1], 
                                             blue: rgbValues[i]);

                    // I don't know what kind of comparison I need to do with the pixels, 
                    // so I don't know how to proceed here to determine whether the image is or is not grayscale.
                    // ...

                }

                return result;
            }
    }
}


推荐答案

我建议使用Presentation Core的 System.Windows.Media.Imaging ,它公开了抽象的 BitmapDecoder类,Windows Imaging直接支持的所有解码器的基础:

What I propose is using Presentation Core's System.Windows.Media.Imaging, which exposes a the abstract BitmapDecoder class, base of all decoders that Windows Imaging directly supports:

System.Windows.Media.Imaging.BmpBitmapDecoder

System.Windows.Media.Imaging.GifBitmapDecoder

System.Windows.Media.Imaging.IconBitmapDecoder

系统。 Windows.Media.Imaging.JpegBitmapDecoder

System.Windows.Media.Imaging.LateBoundBitmapDecoder

System.Windows.Media.Imaging.PngBitmapDecoder

系统.Windows.Media.Imaging.TiffBitmapDecoder

System.Windows.Media.Imaging.WmpBitmapDecoder

System.Windows.Media.Imaging.BmpBitmapDecoder
System.Windows.Media.Imaging.GifBitmapDecoder
System.Windows.Media.Imaging.IconBitmapDecoder
System.Windows.Media.Imaging.JpegBitmapDecoder
System.Windows.Media.Imaging.LateBoundBitmapDecoder
System.Windows.Media.Imaging.PngBitmapDecoder
System.Windows.Media.Imaging.TiffBitmapDecoder
System.Windows.Media.Imaging.WmpBitmapDecoder

解码图像文件流时,将正确的解码器从抽象类转换为特定类。

When decoding an Image file stream, the correct decoder is cast from the abstract class to the specific class.

已解码的图像帧被强制转换为 BitmapFrame类,其成员转换为 BitmapSource类,它引用了有关图像流的所有解码信息。

The decoded Image frames are cast to a BitmapFrame Class, whose members translate to a BitmapSource class, which references all the decoded informations about the Image Stream.

在这种情况下,有趣的是 BitmapSource.Format 属性,使用 System.Windows .Media.PixelFormat结构及其可识别格式的枚举。

Of interest, in this case, is the BitmapSource.Format property, which exposes a System.Windows.Media.PixelFormat Structure and its enumeration of recognized formats.

另请参见 PixelFormats属性

这些格式包括:

These formats include:

PixelFormats.Gray32Float

PixelFormats.Gray16

< a href = https://msdn.microsoft.com/zh-CN/library/system.windows.media.pixelformats.gray8(v=vs.110).aspx rel = nofollow noreferrer> PixelFormats.Gray8

PixelFormats.Gray4 < br>
PixelFormats.Gray2

PixelFormats.Gray32Float
PixelFormats.Gray16
PixelFormats.Gray8
PixelFormats.Gray4
PixelFormats.Gray2

这些标志可以像往常一样进行测试。

These flags can be tested as usual.



此类可用于收集有关位图格式的信息。

我已包含一个属性, IsGrayscale ,它使用先前列出的PixelFormats返回Image PixelFormat的测试结果。


This class can be used to collect informations about a Bitmap format.
I've included a Property, IsGrayscale, which returns the result of the test of the Image PixelFormat, using the PixelFormats previously listed.

图像格式为由 BitmapInfo.Format 引用的 BitmapInfo.Metadata.Format 属性(用于比较的不同来源。

其他属性非常容易解释。

The Image Format is referenced by the BitmapInfo.Format BitmapInfo.Metadata.Format properties (different sources, for comparison.
The other properties are quite self-explanatory.

实现此类的项目必须引用:

A Project that implements this class must reference:

PresentationCore
System.Xaml
WindowsBase



属性:

ImageSize            (Size)          => Size of the Image
Dpi                  (Size)          => DpiX and DpiY of the Image
PixelSize            (Size)          => Size in Pixels ot the Image
Masks                (List)          => List of Byte Masks
BitsPerPixel         (int)           => Bits per Pixel
PixelFormat          (PixelFormat)   => Pixel format as reported by the Decoder
ImageType            (string)        => Textual expression of the image format (GIF, JPG etc.)
HasPalette           (bool)          => The Image has a Palette
Palette              (BitmapPalette) => Palette representation of the Image Colors
HasThumbnail         (bool)          => The Image includes a Thumbnail image
Thumbnail            (BitmapImage)   => The Image Thumbnail, in BitmapImage format
Frames               (int)           => Number of frames. Animated Images are represented by a sequence of frames
FramesContent        (FramesInfo)    => Informations about all frame included in this Image
IsMetadataSuppported (bool)          => The Image has Metadata informations
Metadata             (MetadataInfo)  => Class referencing all the Metadata informations a Image contains
AnimationSupported   (bool)          => This Format supports frame Animations
Animated             (bool)          => The Image is a timed sequence of frames


方法:

public enum DeepScanOptions : int  {
    Default = 0,
    Skip,
    Force
}

public bool IsGrayScale(DeepScanOptions DeepScan)

根据给定的图像内部调色板,检查图像 PixelFormat 是否被视为GrayScale。 DeepScanOptions 枚举器用于确定如何执行扫描。

样本用法部分中的更多详细信息。

Checks whether the Image PixelFormat is to be considered GrayScale, given the image internal Palette. The DeepScanOptions enumerator is used to determine how the scan is performed.
More details in the Sample Usage part.

public enum GrayScaleInfo : int {
    None = 0,
    Partial, 
    GrayScale,
    Undefined
}

public ImagingBitmapInfo.GrayScaleInfo IsGrayScaleFrames()

报告框架面板的状态。可能会返回:

Reports the status of the Frames Palettes. It might return:

:图像没有灰度框

Partial :某些帧是GrayScale

GrayScale :所有框架都具有灰度调色板

Undefined :图像可能没有调色板信息。图片像素格式由 PixelFormat 属性

public ImagingBitmapInfo.GrayScaleStats GrayScaleSimilarity();

此方法执行统计评估(平均值,(Sum(Min)< => Sum(Max )),请考虑图像的所有内部调色板的颜色,以验证可以将内部彩色表示形式吸收到灰度模式中的方法。

它返回 ImagingBitmapInfo.GrayScaleStats ,其中包含以下属性:

This method performs a statistical evaluation (Average, (Sum(Min) <=> Sum(Max)), considering the Colors of all the internal Palettes of an Image, to verify how much the internal colorific representation can be assimilated to a Grayscale pattern.
It returns a ImagingBitmapInfo.GrayScaleStats, which exposes these Properties:

int调色板 :评估的调色板数量

float AverageMaxDistance :RGB组件之间的平均距离(最大)

float AverageMinDistance :RGB组件之间的平均距离(最小值)

float AverageLogDistance :RGB组件之间的平均逻辑距离

float GrayScalePercent :相似度百分比

float GrayScaleAveragePercent :逻辑相似度的百分比y

int Palettes: Number of Palette evaluated
float AverageMaxDistance: Average distance (Max) between RGB components
float AverageMinDistance: Average distance (Min) between RGB components
float AverageLogDistance: Average logical distance between RGB components
float GrayScalePercent: Percentage of similatity
float GrayScaleAveragePercent: Percentage of logical similarity

List< FrameStat> PerFrameValues :该类报告每个Palette条目的计算结果。它公开了以下属性:

List<FrameStat> PerFrameValues: Class that reports the calculated results for each Palette entry. It exposes these Properties:

int ColorEntries :中的颜色数当前调色板

float DistanceMax :RGB组件之间的距离(最大值)

float DistanceMin :RGB分量之间的距离(最小值)

float DistanceAverage :RGB分量之间的平均距离

int ColorEntries: Number of Colors in the current Palette
float DistanceMax: Distance (Max) between RGB components
float DistanceMin: Distance (Min) between RGB components
float DistanceAverage: Average distance between RGB components

public void FrameSourceAddRange(BitmapFrame[] bitmapFrames)

将所有图像帧信息插入 FramesInfo 类。

内部使用,但是当主类的实例 ImagingBitmapInfo 已创建。公开以下属性:

Inserts all Image Frames information in a FramesInfo Class.
It's used internally, but can be filled manually when an instance of the main class, ImagingBitmapInfo, is created. Exposes these properties:

FramesTotalNumber :图像中包含的帧总数< br>
FramesColorNumber :具有调色板的帧数

FramesGrayscaleNumber :灰度帧数

FramesBlackWhiteNumber :黑白框架

FramesTotalNumber: Total number od Frames included in the Image
FramesColorNumber: Number of frames that have a Color Palette
FramesGrayscaleNumber: Number of GrayScale Frames
FramesBlackWhiteNumber: Number of B&W Frames

List< Frames> :类别列表所有帧。 FramesInfo 类对象公开以下属性:

List<Frames>: Class List of all frames. The FramesInfo Class object Exposes these properties:

FrameSize :框架的大小

FrameDpi :框架的DpiX和DpiY
PixelFormat :帧的PixelFormat

IsColorFrame :框架具有调色板

IsGrayScaleFrame :框架具有灰度调色板

IsBlackWhiteFrame :框架具有黑白调色板

FrameSize: Size of the Frame
FrameDpi: DpiX and DpiY of the Frame PixelFormat: PixelFormat of the Frame
IsColorFrame: The frame has a Color Palette
IsGrayScaleFrame: The frame has a GrayScale Palette
IsBlackWhiteFrame: The frame has a B&W Palette

public System.Drawing.Bitmap ThumbnailToBitmap()

转换 System.Windows.Media.Imaging BitmapImage System.Drawing 位图格式,可以在 WinForms 控件/类。 (目前未正确测试)。

Converts a System.Windows.Media.Imaging BitmapImage in a System.Drawing Bitmap format that can be used in WinForms Controls/Classes. (Not properly tested at this time).



示例用法:

主类 ImagingBitmapInfo 已初始化,将文件路径或文件流传递给 BitmapFormatInfo()方法。


Sample usage:
The main class, ImagingBitmapInfo, is initialized passing to the BitmapFormatInfo() method a File Path or a File Stream.

ImagingBitmapInfo BitmapInfo = BitmapFormatInfo(@"[ImagePath]");
//or 
ImagingBitmapInfo BitmapInfo = BitmapFormatInfo([FileStream]);

验证图像是否具有灰度 PixelFormat ,调用 IsGrayScale(ImagingBitmapInfo.DeepScanOptions)方法,指定必须检索此信息。

To verify whether the Image has a GrayScale PixelFormat, call the IsGrayScale(ImagingBitmapInfo.DeepScanOptions) method, specifying thow this information must be retrieved.

ImagingBitmapInfo.DeepScanOptions.Default

该类根据图像像素格式决定是否执行深度扫描图像调色板的颜色(如果存在调色板)。如果像素格式已经报告了灰度图像(例如 PixelFormats.Gray32Float PixelFormats.Gray16 等),则显示深不执行扫描。如果像素格式是索引格式,则执行扫描;否则,将执行扫描。如果PixelFormat是彩色格式,则不执行扫描。

ImagingBitmapInfo.DeepScanOptions.Default
The class decides, based on the Image Pixel Format, whether to perform a Deep Scan of the image Color Palette (if a Palette is present). If the Pixel Format already reports a GrayScale Image (e.g. PixelFormats.Gray32Float, PixelFormats.Gray16 etc.), a Deep scan is not performed. If the Pixel Format is an Indexed one, the scan is performed; if the PixelFormat is Color format, the scan is not performed.


请注意,某些图像(大多数为Gif)可能会报告颜色 PixelFormat
,而内部格式(调色板)可能是GrayScale。

Note that some Images (Gifs, mostly) may report a Color PixelFormat, while the inner format (Palette) might be GrayScale.

ImagingBitmapInfo.DeepScanOptions.Force

指示对所有调色板执行深度扫描帧,无论图像解码器报告了什么 PixelFormat

用于发现报告的彩色图像是否具有一个或多个灰度帧。

ImagingBitmapInfo.DeepScanOptions.Force
Instructs to execute a Deep Scan of the Palettes of all Frames, no matter what PixelFormat is reported by the Image decoder.
Used to discover if a reported Color Image has one or more Grayscale Frames.

ImagingBitmapInfo.DeepScanOptions.Skip

指示考虑到臭味像素格式,即使对色板进行正常扫描也不会执行。

ImagingBitmapInfo.DeepScanOptions.Skip
Instructs to not perform a Deep scan of the Palettes, even if it would be normally performed, given the smelly Pixel Format.

System.Windows.Media.PixelFormat pixelFormat = BitmapInfo.PixelFormat;
bool BitmapIsGrayscale = BitmapInfo.IsGrayScale(ImagingBitmapInfo.DeepScanOptions.Force);



如果结果与预期的不同,请完整检查可以调用以下图像帧 PixelFormat

ImagingBitmapInfo.GrayScaleInfo GrayScaleFrames = BitmapInfo.IsGrayScaleFrames();

此方法对所有框架进行完整检查,并报告内部任何框架是否具有GrayScale PixelFormat 。结果可以是 GrayScaleInfo 枚举值之一:

Partial GrayScale 未定义

如果结果是 GrayScale ,所有内部框架都具有GrayScale PixelFormat

Undefined 表示图像没有调色板信息。

This method performs a complete check of all Frames and reports if any of the internal Frames have a GrayScale PixelFormat. The result can be one of the GrayScaleInfo enumerator values:
None, Partial, GrayScale, Undefined.
If the result is GrayScale, all internal Frames have a GrayScale PixelFormat.
Undefined means that the Image has no Palette informations.

创建图像调色板颜色的灰度相似度的统计表示条目,调用 GrayScaleSimilarity() 方法:

To create a statistical representation of Grayscale similarity of an Image Palettes' Color entries, call the GrayScaleSimilarity() method:

ImagingBitmapInfo.GrayScaleStats Stats = BitmapInfo.GrayScaleSimilarity();

float GrayScalePercent = Stats.GrayScalePercent
float RGBAverageDistancePercent = Stats.GrayScaleAveragePercent
float RGBPatternMaxDistance = Stats.AverageMaxDistance


using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

public class ImagingBitmapInfo
{
    FramesInfo framesInfo;

    public ImagingBitmapInfo()
    {
        this.framesInfo = new FramesInfo();
        this.Metadata = new MetadataInfo();
        this.Metadata.ApplicationName = string.Empty;
        this.Metadata.Author = new List<string>() {  };
        this.Metadata.CameraManufacturer = string.Empty;
        this.Metadata.CameraModel = string.Empty;
        this.Metadata.Comment = string.Empty;
        this.Metadata.Copyright = string.Empty;
        this.Metadata.DateTaken = string.Empty;
        this.Metadata.Subject = string.Empty;
        this.Metadata.Title = string.Empty;
    }

    public Size ImageSize { get; set; }
    public Size Dpi { get; set; }
    public Size PixelSize { get; set; }
    public List<PixelFormatChannelMask> Masks { get; set; }
    public int BitsPerPixel { get; set; }
    public PixelFormat PixelFormat { get; set; }
    public string ImageType { get; set; }
    public bool HasPalette { get; set; }
    public BitmapPalette Palette { get; set; }
    public bool HasThumbnail { get; set; }
    public BitmapImage Thumbnail { get; set; }
    public int Frames { get; set; }
    public FramesInfo FramesContent
    { get { return this.framesInfo; } }
    public bool IsMetadataSuppported { get; set; }
    public MetadataInfo Metadata { get; set; }
    public bool AnimationSupported { get; set; }
    public bool Animated { get; set; }

    public enum DeepScanOptions : int
    {
        Default = 0,
        Skip,
        Force
    }

    public enum GrayScaleInfo : int
    {
        None = 0,
        Partial, 
        GrayScale,
        Undefined
    }

    public System.Drawing.Bitmap ThumbnailToBitmap()
    {
        if (this.Thumbnail == null)
            return null;
        using (System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(
                         this.Thumbnail.DecodePixelWidth, 
                         this.Thumbnail.DecodePixelHeight))
        using (MemoryStream outStream = new MemoryStream())
        {
            BitmapEncoder encoder = new BmpBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(this.Thumbnail));
            encoder.Save(outStream);
            return (System.Drawing.Bitmap)System.Drawing.Bitmap.FromStream(outStream);
        }
    }

    public void FrameSourceAddRange(BitmapFrame[] bitmapFrames)
    {
        if (bitmapFrames == null) return;

        this.framesInfo.Frames.AddRange(bitmapFrames.Select(bf => new FramesInfo.Frame() 
        { 
            Palette = bf.Palette,
            FrameSize = new Size(bf.PixelWidth, bf.PixelHeight),
            FrameDpi = new Size(bf.DpiX, bf.DpiY),
            PixelFormat = bf.Format,
            IsGrayScaleFrame = CheckIfGrayScale(bf.Format, bf.Palette, DeepScanOptions.Force),
            IsBlackWhiteFrame = (bf.Format == PixelFormats.BlackWhite)
        }));

        this.framesInfo.Frames.Where(f => (!f.IsGrayScaleFrame & !f.IsBlackWhiteFrame))
                              .All(f => f.IsColorFrame = true);
    }

    public GrayScaleInfo IsGrayScaleFrames()
    {
        if (this.framesInfo.Frames.Count == 0)
            return GrayScaleInfo.Undefined;
        if (this.framesInfo.FramesGrayscaleNumber > 0)
            return (this.framesInfo.FramesGrayscaleNumber == this.framesInfo.FramesTotalNumber)
                ? GrayScaleInfo.GrayScale : GrayScaleInfo.Partial;
        return GrayScaleInfo.None;
    }

    public bool IsGrayScale(DeepScanOptions DeepScan)
    {
        return CheckIfGrayScale(this.PixelFormat, this.Palette, DeepScan);
    }

    private bool CheckIfGrayScale(PixelFormat pixelFormat, BitmapPalette palette, DeepScanOptions DeepScan)
    {
        if (pixelFormat == PixelFormats.Gray32Float ||
            pixelFormat == PixelFormats.Gray16 ||
            pixelFormat == PixelFormats.Gray8 ||
            pixelFormat == PixelFormats.Gray4 ||
            pixelFormat == PixelFormats.Gray2)
        {
            if (palette == null || (DeepScan != DeepScanOptions.Force)) { return true; }
        }

        if (pixelFormat == PixelFormats.Indexed8 ||
            pixelFormat == PixelFormats.Indexed4 ||
            pixelFormat == PixelFormats.Indexed2)
        {
            DeepScan = (DeepScan != DeepScanOptions.Skip) ? DeepScanOptions.Force : DeepScan;
        }

        if ((DeepScan != DeepScanOptions.Skip) & palette != null)
        {
            List<Color> IndexedColors = palette.Colors.ToList();
            return IndexedColors.All(rgb => (rgb.R == rgb.G && rgb.G == rgb.B && rgb.B == rgb.R));
        }
        return false;
    }

    public GrayScaleStats GrayScaleSimilarity()
    {
        if (!this.HasPalette) return null;

        GrayScaleStats stats = new GrayScaleStats();
        float AccumulatorMax = 0F;
        float AccumulatorMin = 0F;
        float AccumulatorAvg = 0F;
        float[] Distance = new float[3];

        stats.Palettes = this.Frames;

        foreach (FramesInfo.Frame frame in this.framesInfo.Frames)
        {
            GrayScaleStats.FrameStat framestat = new GrayScaleStats.FrameStat() 
            { ColorEntries = frame.Palette.Colors.Count };

            foreach (Color pEntry in frame.Palette.Colors)
            {
                if (!(pEntry.R == pEntry.G && pEntry.G == pEntry.B && pEntry.B == pEntry.R))
                {
                    Distance[0] = Math.Abs(pEntry.R - pEntry.G);
                    Distance[1] = Math.Abs(pEntry.G - pEntry.B);
                    Distance[2] = Math.Abs(pEntry.B - pEntry.R);
                    AccumulatorMax += (float)(Distance.Max());
                    AccumulatorMin += (float)(Distance.Min());
                    AccumulatorAvg += (float)(Distance.Average());
                }
            }
            framestat.DistanceMax = (float)((AccumulatorMax / 2.56) / framestat.ColorEntries);
            framestat.DistanceMin = (float)((AccumulatorMin / 2.56) / framestat.ColorEntries);
            framestat.DistanceAverage = (float)((AccumulatorAvg / 2.56) / framestat.ColorEntries);
            stats.PerFrameValues.Add(framestat);
            AccumulatorMax = 0F;
            AccumulatorMin = 0F;
            AccumulatorAvg = 0F;
        }
        stats.AverageMaxDistance = stats.PerFrameValues.Max(mx => mx.DistanceMax);
        stats.AverageMinDistance = stats.PerFrameValues.Min(mn => mn.DistanceMin);
        stats.AverageLogDistance = stats.PerFrameValues.Average(avg => avg.DistanceAverage);
        stats.GrayScaleAveragePercent = 100F - stats.AverageLogDistance;
        stats.GrayScalePercent = 100F - ((stats.AverageMaxDistance - stats.AverageMinDistance) / 2);
        return stats;
    }

    public class GrayScaleStats
    {
        public GrayScaleStats()
        {
            this.PerFrameValues = new List<FrameStat>();
        }

        public List<FrameStat> PerFrameValues { get; set; }
        public int Palettes { get; set; }
        public float AverageMaxDistance { get; set; }
        public float AverageMinDistance { get; set; }
        public float AverageLogDistance { get; set; }
        public float GrayScalePercent { get; set; }
        public float GrayScaleAveragePercent { get; set; }

        public class FrameStat
        {
            public int ColorEntries { get; set; }
            public float DistanceMax { get; set; }
            public float DistanceMin { get; set; }
            public float DistanceAverage { get; set; }
        }
    }

    public class FramesInfo
    {
        public FramesInfo()
        {
            this.Frames = new List<Frame>();
        }

        public int FramesTotalNumber
        {
            get { return (this.Frames != null) ? this.Frames.Count() : 0; }
            private set { }
        }

        public int FramesColorNumber
        {
            get { return (this.Frames != null) ? this.Frames 
                              .Where(f => f.IsColorFrame == true)
                              .Count() : 0; }
            private set { }
        }
        public int FramesGrayscaleNumber
        {
            get {return (this.Frames != null) ? this.Frames
                             .Where(f => f.IsGrayScaleFrame == true)
                             .Count() : 0; }
            private set { }
        }

        public int FramesBlackWhiteNumber
        {
            get { return (this.Frames != null) ? this.Frames
                              .Where(f => f.IsBlackWhiteFrame == true)
                              .Count() : 0; }
            private set { }
        }

        public List<Frame> Frames { get; private set; }

        internal class Frame
        {
            public BitmapPalette Palette { get; set; }
            public Size FrameSize { get; set; }
            public Size FrameDpi { get; set; }
            public PixelFormat PixelFormat { get; set; }
            public bool IsColorFrame { get; set; }
            public bool IsGrayScaleFrame { get; set; }
            public bool IsBlackWhiteFrame { get; set; }
        }
    }

    public class MetadataInfo
    {
        public string ApplicationName { get; set; }
        public List<string> Author { get; set; }
        public string Copyright { get; set; }
        public string CameraManufacturer { get; set; }
        public string CameraModel { get; set; }
        public string Comment { get; set; }
        public string Format { get; set; }
        public string Subject { get; set; }
        public string Title { get; set; }
        public string DateTaken { get; set; }
        public int Rating { get; set; }
    }
}


public static ImagingBitmapInfo BitmapPixelFormat(string FileName)
{
    using (FileStream stream = File.Open(FileName, FileMode.Open, FileAccess.Read, FileShare.None))
    {
        return BitmapPixelFormat(stream);
    }
}

public static ImagingBitmapInfo BitmapPixelFormat(FileStream stream)
{
    ImagingBitmapInfo imageInfo = new ImagingBitmapInfo();
    var bitmapDecoder = BitmapDecoder.Create(stream, 
                                                BitmapCreateOptions.PreservePixelFormat, 
                                                BitmapCacheOption.Default);

    BitmapSource bitmapSource = bitmapDecoder.Frames[0];
    ImageMetadata imageMetadata = bitmapSource.Metadata;
    BitmapMetadata bitmapMetadata = (BitmapMetadata)bitmapSource.Metadata;

    try
    {
        imageInfo.Frames = bitmapDecoder.Frames.Count();
        if (imageInfo.Frames > 0)
            imageInfo.FrameSourceAddRange(bitmapDecoder.Frames.ToArray());

        imageInfo.ImageType = bitmapMetadata.Format.ToUpperInvariant();
        imageInfo.PixelFormat = bitmapSource.Format;
        imageInfo.HasPalette = ((bitmapSource.Palette != null) && (bitmapSource.Palette.Colors.Count > 0)) ? true : false;
        imageInfo.Palette = bitmapSource.Palette;
        imageInfo.ImageSize = new Size((float)bitmapSource.Height, (float)bitmapSource.Width);
        imageInfo.Dpi = new Size((float)bitmapSource.DpiX, (float)bitmapSource.DpiY);
        imageInfo.PixelSize = new Size(bitmapSource.PixelHeight, bitmapSource.PixelWidth);
        imageInfo.Masks = bitmapSource.Format.Masks.ToList();
        imageInfo.BitsPerPixel = bitmapSource.Format.BitsPerPixel;
        imageInfo.AnimationSupported = bitmapDecoder.CodecInfo.SupportsAnimation;
        imageInfo.Animated = (imageInfo.AnimationSupported && (imageInfo.Frames > 1)) ? true : false;
        imageInfo.HasThumbnail = bitmapDecoder.Thumbnail != null;
        if (imageInfo.HasThumbnail)
            imageInfo.Thumbnail = (BitmapImage)bitmapDecoder.Thumbnail.CloneCurrentValue();


        imageInfo.Metadata.Format = bitmapMetadata.Format;
        //If not supported, Catch and set imageInfo.SetMetadataNonSupported()
        imageInfo.Metadata.ApplicationName = bitmapMetadata.ApplicationName;
        imageInfo.Metadata.Author = (bitmapMetadata.Author != null) 
                                  ? bitmapMetadata.Author.ToList<string>() 
                                  : null;
        imageInfo.Metadata.CameraModel = bitmapMetadata.CameraModel;
        imageInfo.Metadata.CameraManufacturer = bitmapMetadata.CameraManufacturer;
        imageInfo.Metadata.CameraModel = bitmapMetadata.Comment;
        imageInfo.Metadata.Copyright = bitmapMetadata.Copyright;
        imageInfo.Metadata.Subject = bitmapMetadata.Subject;
        imageInfo.Metadata.Title = bitmapMetadata.Title;
        imageInfo.Metadata.Rating = bitmapMetadata.Rating;
        imageInfo.Metadata.Format = bitmapMetadata.Format;
        imageInfo.Metadata.DateTaken = bitmapMetadata.DateTaken;

    }
    catch (System.NotSupportedException)
    { imageInfo.IsMetadataSuppported = false; }

    catch (System.Exception ex) { /* Log ex */ throw ex; }

    return imageInfo;
}

Update:

This is, more or less, the same setup, but WinForms oriented.

Meaning that only the System.Drawing assembly is used.

This is, more or less, the same setup, but WinForms oriented.
Meaning that only the System.Drawing assembly is used.

System.Drawing.Imaging has less option (there’s also a nasty bug in GDI+, related to Bitmap Encoders, never corrected) and some informations are not directly available.
The relevant parts are there, anyway.

System.Drawing.Imaging has less option (there's also a nasty bug in GDI+, related to Bitmap Encoders, never corrected) and some informations are not directly available.
The relevant parts are there, anyway.

Grayscale Images are detected correctly, as far as I could test.

Grayscale Images are detected correctly, as far as I could test.


To note that, if an Image has an indexed Palette (e.g. Gif format), the reported ImageFlags Flag ColorSpaceGRAY is never
correct. Nor is the PixelFormat.Format16bppGrayScale.
The only
possible way (that I found) to verify whether the image is a Grayscale
one in this case, is to parse the color Palette. It takes only a few Ticks to complete, annoying nonetheless.

It’s working correctly for other formats.

To note that, if an Image has an indexed Palette (e.g. Gif format), the reported ImageFlags Flag ColorSpaceGRAY is never correct. Nor is the PixelFormat.Format16bppGrayScale.
The only possible way (that I found) to verify whether the image is a Grayscale one in this case, is to parse the color Palette. It takes only a few Ticks to complete, annoying nonetheless.
It's working correctly for other formats.

Same procedure, as listed before.

Can be used this way:

Same procedure, as listed before.
Can be used this way:

ImagingBitmapInfo BitmapInfo = BitmapPixelFormat(@"[ImagePath]");
bool BitmapIsGrayscale = BitmapInfo.IsGrayScale();

or

ImagingBitmapInfo BitmapInfo = BitmapPixelFormat([ImageStream]);
bool BitmapIsGrayscale = BitmapInfo.IsGrayScale();



Code moved to PasteBin for lack of space in this Post body.


Code moved to PasteBin for lack of space in this Post body.

这篇关于如何确定图像在C#或VB.NET中是否为灰度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆