如何在C#或VB.NET中确定图像是否为灰度?

首先


首先,请注意,此问题中给出的答案对所有灰度图像都不起作用,并且还注意到在另一个问题中接受的答案根本没有解释如何确定图像是否为灰度图像,但无论如何它不符合我的需求,因为它似乎只涵盖JPEG和TIFF图像,并假设它们将具有EXIF元数据及其中的必需字段。 (我无法理解为什么人们确定我链接的第一个问题是我所链接的第二个问题的“重复”……)

最后, 这个最后接受的答案缺乏一个工作和演示的代码示例,但无论如何都无济于事,因为作者使用Bitmap.GetPixel()函数引用了缓慢且弃用的方法,但我们应该使用Bitmap.LockBits()函数而是为了获得更高的性能优势。

情景


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

我对图像的数据结构,像素颜色位和那些东西没有多少经验/意识,我只知道非常基础。 所以,如果我错过了重要的信息,我应该提供我将要测试的图像的任何信息,那么请问我,但无论如何要考虑到我想为“所有”类型的图像创建一个通用的解决方案,好吧,不是全部,但至少这些格式:BMP,JPG,GIF和PNG。

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


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

如果它根本不清楚,为了避免我可以做到你可以浪费你的时间:

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

  • 该解决方案必须关注PixelFormat.Format32bppRgb灰度图像。

  • 解决方案一定不能使用Bitmap.GetPixel()函数,而必须使用Bitmap.LockBits()

  • 我不是要求解释,伪代码也没有关于图像结构/格式/像素等文档的外部链接,我要求一个有效的代码示例(当然,如果作者覆盖图像结构/像素技术,总是更好提供除代码之外的基本解释)。

  • 在C#或VB.NET中,选择并不重要。

研究


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

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# (代码转换,未经测试)

 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直接支持的所有解码器的基础:

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

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

解码后的Image帧被转换为BitmapFrame类 ,其成员转换为BitmapSource类 , 该类引用有关图像流的所有已解码信息。

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

另请参见PixelFormats属性

这些格式包括:

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

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


此类可用于收集有关Bitmap格式的信息。
我已经包含了一个IsGrayscale属性,它使用之前列出的PixelFormats返回Image PixelFormat的测试结果。

图像格式由BitmapInfo.Format BitmapInfo.Metadata.Format属性引用(不同的源,用于比较。
其他属性非常明显。

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

 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) 

在给定图像内部调色板的情况下,检查Image PixelFormat是否被视为GrayScale。 DeepScanOptions枚举器用于确定执行扫描的方式。
样本使用部分中的更多详细信息。

 public enum GrayScaleInfo : int { None = 0, Partial, GrayScale, Undefined } public ImagingBitmapInfo.GrayScaleInfo IsGrayScaleFrames() 

报告框架选项板的状态。 它可能会返回:
None :图像没有灰度帧
Partial :某些帧是GrayScale
GrayScale :所有帧都有一个GrayScale Palette
Undefined :图像可能没有调色板信息。 Image像素格式由PixelFormat属性报告

 public ImagingBitmapInfo.GrayScaleStats GrayScaleSimilarity(); 

此方法执行统计评估(平均值,(总和(最小)<=>总和(最大值)),考虑图像的所有内部调色板的颜色,以validation内部颜色表示可以与灰度模式同化多少。
它返回一个ImagingBitmapInfo.GrayScaleStats ,它公开了这些属性:

int Palettes :评估的Palette数量
float AverageMaxDistance :RGB组件之间的平均距离(Max)
float AverageMinDistance :RGB组件之间的平均距离(Min)
float AverageLogDistance :RGB组件之间的平均逻辑距离
float GrayScalePercentfloat GrayScalePercent百分比
float GrayScaleAveragePercent :逻辑相似度的百分比

List PerFrameValues :报告每个Palette条目的计算结果的类。 它公开了这些属性:

int ColorEntries :当前int ColorEntries板中的颜色数
float DistanceMax :RGB组件之间的距离(Max)
float DistanceMin :RGB组件之间的距离(最小值)
float DistanceAverage :RGB组件之间的平均距离

 public void FrameSourceoAddRange(BitmapFrame[] bitmapFrames) 

FramesInfo类中插入所有图像帧信息。
它在内部使用,但可以在创建主类的实例( ImagingBitmapInfo时手动填充。
公开这些属性:

FramesTotalNumber :图像中包含的od帧数
FramesColorNumber :具有调色板的帧数
FramesGrayscaleNumber :GrayScale帧的数量
FramesBlackWhiteNumber :黑白帧数

List :所有帧的类列表。 FramesInfo类对象公开这些属性:

FrameSize :帧的大小
FrameDpi :Frame PixelFormat DpiX和DpiY:帧的PixelFormat
IsColorFrame :框架有一个调色板
IsGrayScaleFrame :框架有一个GrayScale Palette
IsBlackWhiteFrame :框架有一个黑白调色板

 public System.Drawing.Bitmap ThumbnailToBitmap() 

System.Drawing Bitmap格式转换System.Windows.Media.Imaging BitmapImage ,可以在WinForms控件/类中使用。 (此刻未经过适当测试)。


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

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

要validationImage是否具有GrayScale PixelFormat ,请调用IsGrayScale(ImagingBitmapInfo.DeepScanOptions)方法,指定必须检索此信息。

ImagingBitmapInfo.DeepScanOptions.Default
该类根据图像像素格式决定是否对图像调色板执行深度扫描(如果存在调色板)。 如果像素格式已经报告了GrayScale图像(例如PixelFormats.Gray32FloatPixelFormats.Gray16等),则不执行深度扫描。 如果像素格式是索引格式,则执行扫描; 如果PixelFormat是彩色格式,则不执行扫描。

请注意,某些图像(大多数是PixelFormat )可能会报告Color PixelFormat ,而内部格式(Palette)可能是GrayScale。

ImagingBitmapInfo.DeepScanOptions.Force
无论图像解码器报告PixelFormat是什么,都指示对所有帧的调色板执行深度扫描。
用于发现报告的彩色图像是否具有一个或多个灰度帧。

ImagingBitmapInfo.DeepScanOptions.Skip
指示不会执行深色扫描调色板,即使它是正常执行的,给定有臭味的像素格式。

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

如果结果与预期的结果不同,则可以执行对Image Frames PixelFormat的完整检查,调用:

 ImagingBitmapInfo.GrayScaleInfo GrayScaleFrames = BitmapInfo.IsGrayScaleFrames(); 

如果任何内部帧具有GrayScale PixelFormat ,则此方法执行所有帧的完整检查并报告。 结果可以是GrayScaleInfo枚举器值之一:
NonePartialGrayScaleUndefined
如果结果是GrayScale ,则所有内部帧都具有GrayScale PixelFormat
Undefined意味着Image没有Palette信息。

创建一个Grayscale similari tyof一个Image Palettes’Color条目的统计表示,调用GrayScaleSimilarity()方法:

 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() { }; 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 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 FrameSourceoAddRange(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 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(); } public List 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(); } 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 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 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.FrameSourceoAddRange(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() : 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; } 

更新:

这或多或少是相同的设置,但面向WinForms
这意味着只使用System.Drawing程序集。

System.Drawing.Imaging有较少的选项(GDI +中也有一个令人讨厌的错误,与Bitmap编码器有关 ,从未纠正过),有些信息不能直接使用。
无论如何,相关部分都在那里。

就我测试而言,可以正确检测灰度图像。

要注意的是,如果Image具有索引的Palette(例如Gif格式),则报告的ImageFlags Flag ColorSpaceGRAY永远不会正确。 PixelFormat.Format16bppGrayScale也不是。
在这种情况下,validation图像是否为灰度图像的唯一可能方式(我发现)是解析调色板。 完成它只需要几个Ticks ,但仍然很烦人。
它适用于其他格式。

与之前列出的程序相同。
可以这样使用:

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

要么

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

由于此Post主体中缺少空间, 代码已移至PasteBin 。