【问题标题】:BitmapSource.CopyPixel: how to copy only area of interest?BitmapSource.CopyPixel:如何只复制感兴趣的区域?
【发布时间】:2020-10-05 15:27:07
【问题描述】:

我有一个System.Windows.Media.Imaging.BitmapSource,还有一个小的Int32Rect

我只想将矩形中的位图字节复制到缓冲区。

加法:我想使用这个缓冲区对像素值进行计算,在这种情况下:我想计算一个值来指示位图中数据的清晰度:是焦点吗?该计算测量每个像素与相邻像素的差异。 我不想将数据放在其他位图中

为此,我认为我应该使用BitmapSource.CopyPixels

BitmapSource.CopyPixesl:将指定矩形内的位图像素数据复制到一个像素数组中,该数组具有从指定偏移量开始的指定步幅。

假设位图是 100 像素宽,每个像素有 32 位(4 字节,格式 Bgr32)。完整位图的像素索引为:

Stride 0: [000] [001] [002] [003] ...
Stride 1: [100] [101] [102] [103] ...
Stride 2: [200] [201] [202] [203] ... 
Stride 3: [300] [301] [302] [303] ... 
...

步幅 x 从 x * 100 开始。
步幅 x 中的像素 y 从 x * 100 + y 开始

我的兴趣区域从 [2,3] 开始。它宽 2 像素,高 3 像素。
BitmapSource 每个像素有 32 位。每个像素为 4 个字节。
所以我想在索引的字节数组中复制 6 个 RGBA 值:

[202] RGBA values at 00..03
[203] RGBA values at 04..07
[302] RGBA values at 08..11
[303] RGBA values at 12..15
[402] RGBA values at 16..19
[403] RGBA values at 20..23

我使用了以下代码:

BitmapSource bmp = ...                                    // 100 by 100 pixels
Int32Rect rect = new Int32Rect(0, 0, 2, 3);               // start at [0,0], 2 wide, 3 high

int bytesPerPixel = (bmp.Format.BitsPerPixel + 7) / 8;    // 4 bytes per pixel
int stride = bmp.PixelWidth * bytesPerPixel;              // 400
byte[] largeBuffer = new byte[10000];                     // large buffer; TODO: proper length
bmp.CopyPixels(areaOfInterest, largeBuffer, stride, 0);

缓冲区没有按预期填充,填充如下:

bytes 000 .. 007 filled; bytes 008 .. 399 zero
bytes 400 .. 407 filled; bytes 408 .. 799 zero
bytes 800 .. 807 filled; bytes 808 .. end zero

奇怪的是:如果我使用不同的 x,y 矩形,结果仍然在这些位置。 我想也许参数 Stride 是目标缓冲区中的步幅。确实这给了我一个连续的数组,但这些值仍然不是我想要的。

那么我应该怎么做,只将我感兴趣的像素复制到一个连续的缓冲区?

【问题讨论】:

    标签: c# wpf bitmap bitmapsource


    【解决方案1】:

    除了使用 CroppedBitmap 可能比复制矩形并从中创建新位图更容易之外,CopyPixels 方法的文档具有误导性甚至是错误的。

    stride 参数不是用来保存源位图的步幅,而是用来保存裁剪区域的步幅:

    var rect = new Int32Rect(0, 0, 2, 3);
    
    var stride = (rect.Width * bmp.Format.BitsPerPixel + 7) / 8; // here
    
    var buffer = new byte[stride * rect.Height];
    
    bmp.CopyPixels(rect, buffer, stride, 0);
    

    【讨论】:

    • 对不起,我还没有给出评分。我没有时间检查这个解决方案。稍后会回来(并删除此评论)
    【解决方案2】:

    您是否尝试过使用CroppedBitmap

    你的代码应该是这样的:

    var croppedImage = new CroppedBitmap(image, new Int32Rect(0, 0, 2, 3)); 
    

    【讨论】:

    • 我想用像素值进行计算。所以我需要它们在一个数组中。 CroppedBitmap 创建一个新的 BitmapSource。如果我将 CroppedBitmap 复制到缓冲区,我将复制数据两次,不是吗?
    • @HaraldCoppoolse:是的,如果您的目标是获得比 CroppedBitmap 更好的性能,这可能不是最佳解决方案,因为您需要复制缓冲区两次才能将其放入数组中。根据原始帖子,您没有提到性能,因此如果它是一次性操作,那么 CroppedImage 可能是最简单的解决方案。
    • @HaraldCoppoolse 除了复制 24 字节不必担心之外,CroppedBitmap 实际上并没有创建源缓冲区的副本。相反,它会创建一个位图链,其中只有最后的位图会消耗内存。见这里:referencesource.microsoft.com/#PresentationCore/Core/CSharp/…。但是,您是否注意到另一个问题,它解释了您的错误?
    • 当然我简化了我的问题,这样每个人都可以很容易地看到发生了什么。我必须复制的位图的感兴趣区域实际上是必须打印的晶片图像,并且实际上比标准显示程序可以处理的要大。我需要对像素进行计算。这就是为什么我只复制我感兴趣的像素很重要。但这可能仍然是一个大矩形。
    • @HaraldCoppoolse 这告诉我们什么? CroppedBitmap 仍然不会创建原始缓冲区的副本。而且您已经在另一个答案中找到了针对您的特定问题的解决方案!
    猜你喜欢
    • 2013-07-11
    • 2011-08-10
    • 1970-01-01
    • 2012-02-22
    • 2013-03-03
    • 1970-01-01
    • 1970-01-01
    • 2013-10-19
    相关资源
    最近更新 更多