如何确定C#或VB.NET中的图像是否为灰度?
首先
首先,请注意,this问题中给出的答案对于所有灰度图像都不起作用,并且还注意到this other问题中接受的答案根本没有解释如何确定图像是否是灰度图像,但无论如何它确实不适合我的需求,因为它似乎只涵盖JPEG和TIFF图像,并假设它们将具有EXIF元数据及其中的必需字段. (我无法理解为什么人们确定我所链接的第一个问题是我所链接的第二个问题的“重复”……) 最后,this last接受的答案缺乏一个工作和演示代码示例,但无论如何都无济于事,因为作者使用Bitmap.GetPixel()函数引用了缓慢且不赞成的方法,但我们应该使用Bitmap.LockBits()函数代替为了更高的性能效益. 情景 我有一些GIF,JPG,BMP和PNG图像,我需要确定它们是灰度图像还是不是灰度图像.对于GIF文件,我只关心分析第一帧. 我对图像的数据结构,像素颜色位和那些东西没有多少经验/意识,我只知道非常基础.所以,如果我错过了重要的信息,我应该提供我将要测试的图像的任何信息,那么请问我,但无论如何要考虑到我想为“所有”类型的图像创建一个通用的解决方案,好吧,不是全部,但至少这些格式:BMP,GIF和PNG. 在我提到的那些图像格式中,我的最高优先级是GIF图像,这意味着如果能够确定GIF图像是否为灰度的方法不能用于分析其他类型图像的方法,那么我将接受仅涵盖GIF图像像素处理的答案. 题 我认为我的需求很明确:如何确定图像是否为灰度? 如果它根本不清楚,为了避免我可以做到你可以浪费你的时间: >解决方案必须至少适用于GIF图像. (记住,我只关心GIF中的第一帧),但如果提供的解决方案对BMP,JPG和PNG也有效,那么它当然总是更好. 研究 这就是我到目前为止所做的.我试图理解确定图像是否为灰度的点,我也不确定我的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,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,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,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,它公开了Windows Imaging直接支持的所有解码器的抽象
BitmapDecoder class:
System.Windows.Media.Imaging.BmpBitmapDecoder 解码图像文件流时,正确的解码器从抽象类转换为特定类. 解码的图像帧被转换为BitmapFrame Class,其成员转换为BitmapSource class,其引用关于图像流的所有解码信息. 有趣的是,在这种情况下,是BitmapSource.Format属性,它公开了System.Windows.Media.PixelFormat Structure及其识别格式的枚举. 另见PixelFormats Properties 这些格式包括: PixelFormats.Gray32Float 这些标志可以像往常一样进行测试. 此类可用于收集有关Bitmap格式的信息. 图像格式由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() 报告框架选项板的状态.它可能会返回: 无:图像没有灰度帧 public ImagingBitmapInfo.GrayScaleStats GrayScaleSimilarity(); 该方法执行统计评估(平均值,(Sum(Min)< => Sum(Max)),考虑图像的所有内部调色板的颜色,以验证内部颜色表示可以被同化到多少灰度模式. int Palettes:评估的Palette数量 列表与LT; FrameStat> PerFrameValues:报告每个Palette条目的计算结果的类.它公开了这些属性: int ColorEntries:当前选用板中的颜色数 public void FrameSourceAddRange(BitmapFrame[] bitmapFrames) 在FramesInfo类中插入所有图像帧信息. FramesTotalNumber:图像中包含的od帧数 List< Frames>:所有帧的类列表. FramesInfo类对象公开这些属性: FrameSize:帧的大小 public System.Drawing.Bitmap ThumbnailToBitmap() 以System.Drawing位图格式转换System.Windows.Media.Imaging BitmapImage,可以在WinForms控件/类中使用. (此时未经过适当测试). 样品用法: ImagingBitmapInfo BitmapInfo = BitmapFormatInfo(@"[ImagePath]"); //or ImagingBitmapInfo BitmapInfo = BitmapFormatInfo([FileStream]); 要验证Image是否具有GrayScale PixelFormat,请调用IsGrayScale(ImagingBitmapInfo.DeepScanOptions)方法,指定必须检索此信息. ImagingBitmapInfo.DeepScanOptions.Default
ImagingBitmapInfo.DeepScanOptions.Force ImagingBitmapInfo.DeepScanOptions.Skip System.Windows.Media.PixelFormat pixelFormat = BitmapInfo.PixelFormat; bool BitmapIsGrayscale = BitmapInfo.IsGrayScale(ImagingBitmapInfo.DeepScanOptions.Force); 如果结果与预期的结果不同,可以执行完整的图像帧PixelFormat检查,调用: ImagingBitmapInfo.GrayScaleInfo GrayScaleFrames = BitmapInfo.IsGrayScaleFrames(); 此方法执行所有帧的完整检查,并报告任何内部帧是否具有GrayScale PixelFormat.结果可以是GrayScaleInfo枚举器值之一: 要创建图像调色板的颜色条目的灰度相似性的统计表示,请调用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<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,Force } public enum GrayScaleInfo : int { None = 0,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; } 更新: 这或多或少是相同的设置,但面向WinForms. System.Drawing.Imaging有较少的选项(在GDI中也有一个令人讨厌的错误,与Bitmap Encoders相关,从未纠正过)并且一些信息无法直接获得.无论如何,相关部分都在那里. 就我测试而言,可以正确检测灰度图像.
与之前列出的程序相同. ImagingBitmapInfo BitmapInfo = BitmapPixelFormat(@"[ImagePath]"); bool BitmapIsGrayscale = BitmapInfo.IsGrayScale(); 要么 ImagingBitmapInfo BitmapInfo = BitmapPixelFormat([ImageStream]); bool BitmapIsGrayscale = BitmapInfo.IsGrayScale(); Code moved to PasteBin因为这个邮政遗体缺乏空间. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |