获取图像的所有颜色

时间:2020-12-22 21:37:09

标签: .net powershell

Add-Type -AssemblyName System.Windows.Forms 

$pic = [Drawing.Bitmap]::new("D:\image.png")
$gfx = [Drawing.Graphics]::FromImage($pic)

$pic.GetPixel(1,1).name

如何使用 .NET 方法获取每个像素的颜色?一个一个地拿,真的很慢。我想,有一种正确的方法可以快速做到这一点。

我听说,LockBits 或许能帮上忙,但如果是,我不知道怎么写...

1 个答案:

答案 0 :(得分:1)

你确实可以使用LockBits()直接访问位图数据:

using namespace System.Drawing
using namespace System.Runtime.InteropServices

# Load PNG file
$pathToFile = "D:\image.png"
$bmp = [Bitmap]::FromFile($pathToFile)

# Lock all the bits (from top left to bottom right)
$bounds = [Rectangle]::new([Point]::Empty, $bmp.Size)
$data = $bmp.LockBits($bounds, 'ReadOnly', 'Format32bppArgb')

# Scan0 is a pointer to the raw bitmap in local memory
# Let's read the ARGB value at 0,0
$px00 = [Marshal]::ReadInt32($data.Scan0, 0)
$px00color = [Color]::FromArgb($px00)

请注意,由于 Int32 为 32 位 = 4 字节宽,因此下一个 ARGB 值始终为当前偏移量 + 4。例如读取 0,1 处的像素值:

$px01 = [Marshal]::ReadInt32($data.Scan0, 4)

要枚举所有像素值,您可以这样做:

$pixelLength = $bmp.Width * $bmp.Height

$allPixels = for($offset = 0; $offset -lt $pixelLength; $offset++)
{
  [Color]::FromArbg([Marshal]::ReadInt32($data.Scan0, $offset * 4))
}

# don't forget to clean up original bitmap
$bmp.UnlockBits($data)
$bmp.Dispose()

如果您正在处理大图像,由于累积了所有像素数据,这仍然会变慢,因此您可能需要专门针对您的用例优化像素收集过程。

假设我们想要识别图像中的前 25 种颜色 - 在这种情况下,我们不需要收集/存储每个单独的像素值,每个不同颜色只需要一个。为此,哈希表(或任何其他类似字典的数据类型)会更有效:

$pathToFile = "D:\image.png"
$top = 25
$bmp = [System.Drawing.Bitmap]::FromFile($file.FullName)

# Create hashtable to keep track of values seen
$counts = @{}

try {
    # Lock all bits
    $data = $bmp.LockBits([System.Drawing.Rectangle]::new([System.Drawing.Point]::Empty, $bmp.Size), [System.Drawing.Imaging.ImageLockMode]::ReadOnly, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb)
    try {
        $length = $bmp.Width * $bmp.Height
        for($i = 0; $i -lt $length; $i++)
        {
            $argb = [System.Runtime.InteropServices.Marshal]::ReadInt32($data.Scan0, $i * 4)
            # Increase counter per found ARGB value
            $counts[$argb]++
        }
    }
    finally{
        # Clean up our read-only handle
        $bmp.UnlockBits($data)
    }
}
finally{
    # Clean up source bmp
    $bmp.Dispose()
}

# Pick the top 25 values and convert those to [Color]
$topArgbValues = $counts.GetEnumerator() |Sort Value -Descending |Select -First $top
$topArgbValues |Select @{Name='Color';Expression={[System.Drawing.Color]::FromArgb($_.Key)}},@{Name='Count';Expression={$_.Value}}
相关问题