【问题标题】:using Marshal.Copy and LockBits in image processing in C#在 C# 的图像处理中使用 Marshal.Copy 和 LockBits
【发布时间】:2021-11-19 09:22:59
【问题描述】:

我正在尝试编写灰度代码。我基本上是这样写的,但我不明白如何在这段代码中使用 Marshal 或 lockbits 来提高速度。(我选择这种方法是因为使用的指针对我来说更复杂)我写了一些东西,但我知道代码太多或丢失了。我知道我应该使用 lockbits 而不是 getpixel 以加快速度,但我很困惑。这对我来说很复杂,可能我在错误的地方使用它们。

编辑:我正在尝试将照片变为灰色。它正在编译,但没有变灰,我不明白代码有什么问题。

编辑:我知道答案在下面,但我想学习用 Lockbites 和 Marshal.Copy 函数编写这个。

代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace dnm2709normalimg
{
    class GrayFilter
    {
        
        public static Bitmap DoGray(Bitmap bmp)
        {
            for (int i = 0; i < bmp.Height; i++)
            {
                for (int j = 0; j < bmp.Width; j++)
                {
                    int value = (bmp.GetPixel(j, i).R + bmp.GetPixel(j, i).G + bmp.GetPixel(j, i).B) / 3;
                    Color clr;
                    clr = Color.FromArgb(value, value, value);
                    BitmapData bmpData =bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),ImageLockMode.ReadOnly, bmp.PixelFormat);
                    // Get the address of the first line.
                    IntPtr ptr = bmpData.Scan0;

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

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

                    // Set every third value to 255. A 24bpp bitmap will look red.   
                    for (int counter = 2; counter < rgbValues.Length; counter += 3)
                        rgbValues[counter] = 255;

                    // Copy the RGB values back to the bitmap
                    Marshal.Copy(rgbValues, 0, ptr, bytes);
                    bmp.UnlockBits(bmpData);
                }
            }
            return bmp;
        }
    }
}

【问题讨论】:

  • 您的实际问题是什么?你得到什么结果?你会期待什么结果?另外:“应该使用 lockbits 而不是 getpixel”,是的,如果你需要性能。在许多情况下,获取/设置像素非常好,例如,如果代码很少使用或图像很小。
  • 实际上我是这方面的新手,但对于该项目,我应该使用更快的方法,但老实说,我无法清楚地了解主题。很难找到来源,或者我找不到我的主要语言.所以我问这个问题是为了了解我应该使用lockbit和marshall的哪个部分。我在代码中做错了并且正在工作但使用了错误的方式。
  • 但是 什么 的工作方式不对? 得到什么结果?说明代码“工作但以错误的方式”并不能帮助我们提供答案。请更新问题,而不是在 cmets 中添加信息。
  • 好的。我要去那里写。
  • 请用回答问题所需的额外信息更新(编辑)您的问题。祝你好运???

标签: c# image-processing


【解决方案1】:

首先,您循环遍历每个像素,并且每次都处理整个图像。所以去掉外层循环。

// Set every third value to 255. A 24bpp bitmap will look red.   
for (int counter = 2; counter < rgbValues.Length; counter += 3)

不正确。它假定每一行将包含确切数量的像素,没有剩余字节。由于对齐,图像的每一行可能包含一些额外的字节,这就是为什么我们需要一个“步幅”而不仅仅是宽度 + 每个像素的字节数。

此外,在您的示例中,没有计算灰度值。这应该是每个颜色通道的某种平均,通常会使用一些权重来更加强调绿色通道。

所以转换为灰度的代码应该是这样的:

for(var y = 0; y < bmp.Height; y++){
   var row = y * bmpData.Stride;
   for(var x = 0; x < bmp.Width; x++){
        var i = row + x * bytesPerPixel;
        var grayscaleValue = rgbValues[i] * 0.11 + rgbValues[i + 1]*0.59 + rgbValues[i + 2] * 0.3;
        rgbValues[i] = grayscaleValue ;
        rgbValues[i + 1] = grayscaleValue ;
        rgbValues[i + 2] = grayscaleValue ;
    }
}

还要注意,这并不关心rgbValues 是指针还是复制的数组。困难的部分不是使用指针/不安全代码,而是让索引正确。

【讨论】:

  • 感谢您的全面回答,我将尝试用这种方式编写代码。
【解决方案2】:

如果您像 @JonasH 在他的回答中所做的那样访问原始数据,那么您必须知道您的输入 Bitmap 的实际 PixelFormat 是什么。他的答案适用于 24bpp RGB 图像。

适用于所有位图的更一般的答案:

private const float RLum = 0.299f;
private const float GLum = 0.587f;
private const float BLum = 0.114f;


public static Bitmap DoGray(Bitmap bmp)
{
    using var result = new Bitmap(bmp.Width, bmp.Height);
    using (Graphics g = Graphics.FromImage(result))
    {
        // Grayscale color matrix
        var colorMatrix = new ColorMatrix(new float[][]
        {
            new float[] { RLum, RLum, RLum, 0, 0 },
            new float[] { GLum, GLum, GLum, 0, 0 },
            new float[] { BLum, BLum, BLum, 0, 0 },
            new float[] { 0, 0, 0, 1, 0 },
            new float[] { 0, 0, 0, 0, 1 }
        });

        using (var attrs = new ImageAttributes())
        {
            attrs.SetColorMatrix(colorMatrix);
            g.DrawImage(bmp, new Rectangle(0, 0, result.Width, result.Height), 0, 0, result.Width, result.Height, GraphicsUnit.Pixel, attrs);
        }
    }
    return result;
}

但如果您不介意使用library(无耻的自我宣传提醒),它会为您提供fast bitmap data manipulation,它隐藏了解释实际像素格式的复杂性,因此您可以像这样使用它:

public static Bitmap DoGray(Bitmap bmp)
{
    using var result = new Bitmap(bmp.Width, bmp.Height);
    using IReadableBitmapData src = bmp.GetReadableBitmapData();
    using IWritableBitmapData dst = result.GetWritableBitmapData();
    IReadableBitmapDataRow rowSrc = src.FirstRow;
    IWritableBitmapDataRow rowDst = dst.FirstRow;
    do
    {
        for (int x = 0; x < src.Width; x++)
            rowDst[x] = rowSrc[x].ToGray();
    } while (rowSrc.MoveNextRow() && rowDst.MoveNextRow());
    return result;
}

但实际上,该库有一个ToGrayscale 扩展方法,因为它使用并行处理,所以速度更快(测试用例取自here):

==[Performance Test Results]================================================
Test Time: 2,000 ms
Warming up: Yes
Test cases: 3
Calling GC.Collect: Yes
Forced CPU Affinity: No
Cases are sorted by fulfilled iterations (the most first)
--------------------------------------------------
1. ImageExtensions.ToGrayscale: 4,549 iterations in 2,000.20 ms. Adjusted for 2,000 ms: 4,548.55
2. Sequential processing: 1,651 iterations in 2,001.54 ms. Adjusted for 2,000 ms: 1,649.73 (-2,898.82 / 36.27 %)
3. Graphics.DrawImage(..., ImageAttributes): 964 iterations in 2,000.17 ms. Adjusted for 2,000 ms: 963.92 (-3,584.63 / 21.19 %)

更新:以上所有三个选项都返回一个新的Bitmap,采用 32bpp ARGB 像素格式。要使位图灰度到位,您可以使用上述库中的MakeGrayscale 扩展方法。

第二个例子可以很容易地改写原Bitmap就地修改,只需通过GetReadWriteBitmapData扩展方法得到一个IReadWriteBitmapData。该链接实际上包含一个有关如何在适当位置制作Bitmap 灰度的示例。

【讨论】:

  • 谢谢你的回答。它解释得很清楚,但我想我也是理解这些答案的新手。我应该搜索更多。
  • 如果您想实施解决方案,您只需复制并粘贴其中一个答案即可。如果您只想使用一个简单的解决方案,那么您可以使用上述library中的MakeGrayscaleToGrayscale 扩展方法
  • 我指出的答案是有效的,真的很感谢你,但我正在尝试使用 marshal 和 LockBit 来学习这样做,我必须学习这些函数的用法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-13
  • 2016-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多