【问题标题】:Get average RGB values from picture displayed inside Image control in vb.net从 vb.net 中的 Image 控件中显示的图片中获取平均 RGB 值
【发布时间】:2015-07-02 11:32:55
【问题描述】:

我想从Image 控件中显示的图像中获取 R、G 和 B 的平均值。此图像的来源是使用this dll 从网络摄像头捕获的帧。

尽管我发现了一些应该能够做到这一点的函数,但它们要么是 for C# using pointers and unsafe code 要么是 for Windows Forms,我无法让它们与 Image 控件一起使用。

我应该如何进行计算,或者至少将 Image.Source 转换为 Bitmap 以便使用 WinForms 链接中的函数?

【问题讨论】:

    标签: wpf vb.net visual-studio-2013


    【解决方案1】:

    这是一个简单的纯 WPF 解决方案,它直接访问 BitmapSource 的像素缓冲区。它适用于Bgr24Bgr32Bgra32Pbgra32 格式。在Pbgra32 的情况下,所有 alpha 值都应为 255,否则您可能必须将每个像素的(预乘)颜色值除以 alpha / 255

    public Color GetAverageColor(BitmapSource bitmap)
    {
        var format = bitmap.Format;
    
        if (format != PixelFormats.Bgr24 &&
            format != PixelFormats.Bgr32 &&
            format != PixelFormats.Bgra32 &&
            format != PixelFormats.Pbgra32)
        {
            throw new InvalidOperationException("BitmapSource must have Bgr24, Bgr32, Bgra32 or Pbgra32 format");
        }
    
        var width = bitmap.PixelWidth;
        var height = bitmap.PixelHeight;
        var numPixels = width * height;
        var bytesPerPixel = format.BitsPerPixel / 8;
        var pixelBuffer = new byte[numPixels * bytesPerPixel];
    
        bitmap.CopyPixels(pixelBuffer, width * bytesPerPixel, 0);
    
        long blue = 0;
        long green = 0;
        long red = 0;
    
        for (int i = 0; i < pixelBuffer.Length; i += bytesPerPixel)
        {
            blue += pixelBuffer[i];
            green += pixelBuffer[i + 1];
            red += pixelBuffer[i + 2];
        }
    
        return Color.FromRgb((byte)(red / numPixels), (byte)(green / numPixels), (byte)(blue / numPixels));
    }
    

    由于 Image 控件的 Source 属性属于 ImageSource 类型,因此您必须先将其强制转换为 BitmapSource,然后才能将其传递给方法:

    var bitmap = (BitmapSource)image.Source; 
    var color = GetAverageColor(bitmap);
    

    【讨论】:

    • 我把它翻译成VB后效果很好(在线工具没有问题),谢谢!
    【解决方案2】:

    已编辑以支持 WPF 图像控制(仍需要参考 System.Drawing)。

    Public Function GetWPFImageAverageRGB(wpfImage As System.Windows.Controls.Image) As System.Drawing.Color
        Using ms = New IO.MemoryStream()
            Dim encoder = New JpegBitmapEncoder()
            encoder.Frames.Add(BitmapFrame.Create(CType(wpfImage.Source, BitmapImage)))
            encoder.Save(ms)
    
            Using bmp = CType(System.Drawing.Bitmap.FromStream(ms), System.Drawing.Bitmap)
                Dim reds As Long
                Dim greens As Long
                Dim blues As Long
    
                For x = 0 To bmp.Width - 1
                    For y = 0 To bmp.Height - 1
                        With bmp.GetPixel(x, y)
                            reds += .R
                            greens += .G
                            blues += .B
                        End With
                    Next
                Next
    
                Dim count = bmp.Height * bmp.Width
    
                Return System.Drawing.Color.FromArgb(CInt(reds / count), CInt(greens / count), CInt(blues / count))
            End Using
        End Using
    End Function
    

    用法:

    With GetWPFImageAverageRGB(image) 'image is a System.Windows.Controls.image
       Console.WriteLine("Average: R={0}, G={1}, B={2}", .R, .G, .B)
       Console.ReadKey()
    End With
    

    【讨论】:

    • @OneFineDay,是的。
    • 谢谢克雷格。我能够从文件中获取 RGB 平均值,但仍然找不到使用 Image 控件的 Source 的方法。有什么建议吗?另外,就像@OneFineDay 提到的那样,这个双循环有点慢,转换为lockbits 会不会太复杂?
    • 一种或另一种方式,您将遍历所有字节,无论是在双循环中还是通过字节数组。 .GetPixel 是产生任何不利成本的地方(尽管它仍然很活泼)。您有 WPF 图像元素吗?该函数只接受一个 Image 对象,因此它可以直接来自您的元素,而不是使用 Drawing.Image.FromFile() 是的,我也会发布一个 LockBits 实现。
    • 抱歉我的坚持,但如果我只是尝试GetImageAverageRGB(imgCapture)imgCapture 是 Image 对象的名称)我会得到Value of type 'System.Windows.Controls.Image' cannot be converted to 'System.Drawing.Image'
    • @Molx 对此感到抱歉!见编辑。我从带有图像控件和加载的 .PNG 的 WPF 窗口中尝试了此操作。如果有人有更简单、更少迂回的方法来做到这一点,我也很想知道。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-03
    • 2022-01-15
    • 1970-01-01
    • 2011-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多