Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
314 views
in Technique[技术] by (71.8m points)

.net - 分析图像的颜色(Analyze colors of an Image)

I have part of an Image cropped out, and 2 Color Ranges (H/S/L) defined via 12 trackbars.

(我裁剪了一部分图像,并通过12个轨迹栏定义了2个颜色范围(H / S / L)。)

I also have a "Precision/Speed" slider ranging from 1 to 10.

(我还有一个“精度/速度”滑块,范围从1到10。)

I need to analyze how many pixels of the Image fall into each of the specified Color Ranges.

(我需要分析多少个像素的图像属于每个指定的颜色范围。)
Based on the precision/speed slider, I skip some rows/pixels.

(基于精度/速度滑块,我跳过了一些行/像素。)

Its working great but its too slow.

(它的工作很棒,但是太慢了。)

With high precision (trackbar value = 1), it takes about 550 ms.

(高精度(跟踪条值= 1)大约需要550毫秒。)
With low precision but high speed (trackbar value = 10) it takes about 5 ms.

(精度低但速度快(跟踪条值= 10)时,大约需要5毫秒。)

Is there a way to speed this code up?

(有没有办法加快此代码的速度?)

Ideally I would need it to be 5 times faster.

(理想情况下,我需要将其速度提高5倍。)

 For y As Integer = 0 To 395
    If y Mod 2 = 0 Then
        startpixel = tbval / 2
    Else
        startpixel = 0
    End If

    If y Mod tbval = 0 Then
        For x As Integer = 0 To 1370
            If x Mod tbval - startpixel = 0 Then
                analyzedpixels = analyzedpixels + 1

                Dim pColor As Color = crop.GetPixel(x, y)
                Dim h As Integer = pColor.GetHue
                Dim s As Integer = pColor.GetSaturation * 100
                Dim l As Integer = pColor.GetBrightness * 100

                'verify if it is part of the first color

                If h >= h1min And h <= h1max And s >= s1min And s <= s1max And l >= l1min And l <= l1max Then
                    color1pixels = color1pixels + 1
                End If

                If h >= h2min And h <= h2max And s >= s2min And s <= s2max And l >= l2min And l <= l2max Then
                    color2pixels = color2pixels + 1
                End If
            End If
        Next
    End If
Next

EDIT:

(编辑:)

This is the working code..

(这是工作代码。)

Dim rect As New Rectangle(0, 0, crop.Width, crop.Height)
Dim bdata As Imaging.BitmapData = crop.LockBits(rect, Imaging.ImageLockMode.ReadOnly, crop.PixelFormat)

Dim ptr As IntPtr = bdata.Scan0
Dim bytes As Integer = Math.Abs(bdata.Stride) * crop.Height
Dim rgbValues As Byte() = New Byte(bytes - 1) {}
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes)

For i As Integer = 0 To crop.Height - 1

    If i Mod 2 = 0 Then
        startpixel = tbval / 2
    Else
        startpixel = 0
    End If

    If i Mod tbval = 0 Then
        For j As Integer = 0 To crop.Width - 1
            If j Mod tbval - startpixel = 0 Then

                analyzedpixels = analyzedpixels + 1
                Dim position = (bdata.Stride * i) + j * 4
                Dim c = Color.FromArgb(BitConverter.ToInt32(rgbValues, position))
                Dim h As Integer = c.GetHue
                Dim s As Integer = c.GetSaturation * 100
                Dim l As Integer = c.GetBrightness * 100

                If h >= h1min And h <= h1max And s >= s1min And s <= s1max And l >= l1min And l <= l1max Then
                    color1pixels = color1pixels + 1
                End If

                If h >= h2min And h <= h2max And s >= s2min And s <= s2max And l >= l2min And l <= l2max Then
                    color2pixels = color2pixels + 1
                End If
            End If
            stride += 4
        Next
    End If
Next

crop.UnlockBits(bdata)
  ask by sharkyenergy translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

When performing sequential operations on a Bitmap's color data, the Bitmap.LockBits method can provide a huge increase in performace, since the Bitmap data needs to be loaded in memory just once, as opposed to sequential GetPixel/SetPixel calls: each call will load a partial section of the Bitmap data in memory and then discard it, to repeat the process when these methods are called again.

(当对位图的颜色数据执行顺序操作时, Bitmap.LockBits方法可以显着提高性能,因为与连续的GetPixel / SetPixel调用相比,Bitmap数据仅需要一次加载到内存中:每次调用都将加载一个位图数据在内存中的部分区域,然后将其丢弃,以在再次调用这些方法时重复该过程。)

If a single call to GetPixel/SetPixel is needed instead, these methods may have a performace advantage over Bitmap.LockBits() .

(如果需要单次调用GetPixel / SetPixel,则这些方法可能比Bitmap.LockBits()具有性能优势。)

But, in this case, performace is not a factor, in practice.

(但是,在这种情况下,实际上,性能不是一个因素。)

How Bitmap.LockBits() works :

(Bitmap.LockBits()如何工作 :)

This is the function call:

(这是函数调用:)

public BitmapData LockBits (Rectangle rect, ImageLockMode flags, PixelFormat format);
// VB.Net
Public LockBits (rect As Rectangle, flags As ImageLockMode, format As PixelFormat) As BitmapData
  • rect As Rectangle : This parameter specifies the section of the Bitmap data we're interested in;

    (rect As Rectangle :此参数指定我们感兴趣的Bitmap数据部分;)

    this section's bytes will be loaded in memory.

    (该部分的字节将被加载到内存中。)

    It can be the whole size of the Bitmap or a smaller section of it.

    (它可以是位图的整体大小,也可以是其较小部分。)

  • flags As ImageLockMode : Specifies the type of lock to perform.

    (flags As ImageLockMode :指定要执行的锁定的类型。)

    The access to the memory to can be limited to Read or Write, or concurrent Read/Write operations are allowed.

    (对内存的访问可以限制为读或写,或者允许并发的读/写操作。)
    It can be also used to specify - setting ImageLockMode.UserInputBuffer - that the BitmapData object is provided by the calling code.

    (它还可以用于指定-设置ImageLockMode.UserInputBuffer调用代码提供BitmapData对象。)
    The BitmapData object defines some of the Bitmap properties ( Width and Height of the Bitmap, width of the scan line (the Stride : number of bytes that compose a single line of pixels, represented by the Bitmap.Width multiplied by the number of bytes per pixel, rounded to a 4-bytes boundary. See the note about the Stride ).

    (BitmapData对象定义一些位图属性(位图的WidthHeight ,扫描线的宽度( Stride :组成单行像素的字节数,由Bitmap.Width表示。 Bitmap.Width乘以每字节的字节数)像素,四舍五入到4个字节的边界。请参见有关Stride的注释)。)
    The BitmapData.Scan0 property is the Pointer ( IntPtr ) to the initial memory location where the Bitmap data is stored.

    (BitmapData.Scan0属性是指向初始存储位置的指针( IntPtr ),该位置存储了位图数据。)
    This property allows to specify the memory location where a pre-existing Bitmap data buffer is already stored.

    (该属性允许指定已经存储了预先存在的位图数据缓冲区的存储位置。)

    It becomes useful when Bitmap data is exchanged between processes using Pointers.

    (当使用指针在进程之间交换位图数据时,它将变得很有用。)
    Note that the MSDN documentation about ImageLockMode.UserInputBuffer is confusing (if not wrong).

    (请注意,有关ImageLockMode.UserInputBuffer的MSDN文档令人困惑(如果没有错)。)

  • format As PixelFormat : the format used to describe the Color of a single Pixel.

    (format As PixelFormat :用于描述单个Pixel的颜色的格式。)

    It translates, in practice, in the number of bytes used to represent a Color.

    (实际上,它转换为用于表示颜色的字节数。)
    When PixelFormat = Format24bppRgb , each Color is represented by 3 bytes (RGB values).

    (当PixelFormat = Format24bppRgb ,每种颜色都由3个字节(RGB值)表示。)

    With PixelFormat.Format32bppArgb , each Color is represented by 4 bytes (RGB values + Alpha).

    (使用PixelFormat.Format32bppArgb ,每种颜色都由4个字节(RGB值+ Alpha)表示。)
    Indexed formats, as Format8bppIndexed , specify that each byte value is the index to a Palette entry.

    (索引格式(如Format8bppIndexed )指定每个字节值是Palette条目的索引。)

    The Palette is part of the Bitmap information, except when the pixel format is PixelFormat.Indexed : in this case, each value is an entry in the System color table.

    (Palette是位图信息的一部分,除非像素格式为PixelFormat.Indexed :在这种情况下,每个值都是系统颜色表中的一个条目。)
    The default PixelFormat of a new Bitmap object, if not specified, is PixelFormat.Format32bppArgb , or PixelFormat.Canonical .

    (如果未指定,则新Bitmap对象的默认PixelFormatPixelFormat.Format32bppArgbPixelFormat.Canonical 。)

Important notes about the Stride:

(关于Stride的重要说明:)

As mentioned before, the Stride (also called scan-line) represents the number of bytes that compose a single line of pixels.

(如前所述, Stride (也称为扫描线)代表组成一行像素的字节数。)

Because of harware alignment requirements, it's always rounded up to a 4-bytes boundary (an integer number multiple of 4).

(由于硬件对齐要求,它总是四舍五入为4个字节的边界(4的整数倍)。)

Stride =  [Bitmap Width] * [bytes per Color]
Stride += (Stride Mod 4) * [bytes per Color]

This is one of the reasons why we always work with Bitmaps created with PixelFormat.Format32bppArgb : the Bitmap's Stride is always already aligned to the required boundary.

(这就是为什么我们始终使用通过PixelFormat.Format32bppArgb创建的位图的原因之一:位图的Stride始终已经与所需边界对齐。)

What if the Bitmap's format is instead PixelFormat.Format24bppRgb (3 bytes per Color) ?

(如果位图的格式改为 PixelFormat.Format24bppRgb (每种颜色3个字节)怎么办?)

If the Bitmap's Width multiplied by the Bytes per Pixels is not a multiple of 4 , the Stride will be padded with 0 s to fill the gap.

(如果位图的Width乘以每个像素的字节数不是4的倍数,则将用0 s填充Stride来填补空白。)

A Bitmap of size (100 x 100) will have no padding in both 32 bit and 24 bit formats:

(大小为(100 x 100)位图在32位和24位格式中都不会填充:)

100 * 3 = 300 : 300 Mod 4 = 0 : Stride = 300
100 * 4 = 400 : 400 Mod 4 = 0 : Stride = 400

It will be different for a Bitmap of size (99 x 100) :

(大小(99 x 100)的位图会有所不同:)

99 * 3 = 297 : 297 Mod 4 = 1 : Stride = 297 + ((297 Mod 4) * 3) = 300
99 * 4 = 396 : 396 Mod 4 = 0 : Stride = 396

The Stride of a 24 bit Bitmap is padded adding 3 bytes (set to 0 ) to fill the boundary.

(填充24位位图的Stride ,添加3个字节(设置为0 )以填充边界。)

It's not a problem when we inspect/modify internal values accessing single Pixels by their coordinates, similar to how SetPixel/GetPixel operate: the position of a Pixel will always be found correctly.

(当我们检查/修改内部值以通过其坐标访问单个Pixel时,这不是问题 ,类似于SetPixel / GetPixel的操作方式:总是可以正确找到Pixel的位置。)

Suppose we need to inspect/change a Pixel at position (98, 70) in a Bitmap of size (99 x 100) .

(假设我们需要检查/更改大小为(99 x 100)的位图中位置(98, 70) 98,70)处的Pixel。)
Considering only the bytes per pixel.

(仅考虑每个像素的字节数。)

The pixel position inside the Buffer is:

(缓冲区内的像素位置为:)

[Bitmap] = new Bitmap(99, 100, PixelFormat = Format24bppRgb)

[Bytes x pixel] = Image.GetPixelFormatSize([Bitmap].PixelFormat) / 8
[Pixel] = new Point(98, 70)
[Pixel Position] = ([Pixel].Y * [BitmapData.Stride]) + ([Pixel].X * [Bytes x pixel])
[Color] = Color.FromArgb([Pixel Position] + 2, [Pixel Position] + 1, [Pixel Position])

Multiplying the Pixel's vertical position by the width of the scan line, the position inside the buffer will always be correct: the padded size is included in the calculation.

(将像素的垂直位置乘以扫描线的宽度,缓冲区内的位置将始终是正确的:计算中包括填充大小。)
The Pixel Color at the next position, (0, 71) , will return the expected results:

(下一个位置(0, 71)的像素颜色将返回预期的结果:)

It will be different when reading color bytes sequentially.

(顺序读取颜色字节时会有所不同。)
The first scan line will return valid results up to the last Pixel (the last 3 bytes): the next 3 bytes will return the value of the bytes used to round the Stride , all set to 0 .

(第一行扫描将返回有效的结果,直到最后一个像素(最后3个字节):接下来的3个字节将返回用于将“ Stride取整的字节的值,都设置为<code


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...