【问题标题】:Get all colors of an image获取图像的所有颜色
【发布时间】:2020-12-23 22:27:06
【问题描述】:
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 或许可以帮到这里,但如果是的话,我不知道怎么写……

【问题讨论】:

  • GetPixel 很慢,因为它依次锁定和解锁每个像素,锁定一堆像素会更快,但您确实需要更好地指定实际输出的内容。在 1920x1080 图像中,将 200 万个颜色名称 (?) 打印到控制台不是很可行。您将如何处理所有这些颜色值?
  • 如果是 C# 代码 - 您可以并行处理像素。然而,这似乎更像是一个决策问题。您真的需要查看每个像素并注意颜色吗?你不能从每一行中抽取一些样本吗?实际目标是什么?
  • Stack Overflow 是一个问答网站,而不是代码翻译服务。尝试先自己翻译代码,然后在遇到困难时来找我们,确保向我们展示您尝试过的内容并创建minimal reproducible example

标签: .net powershell


【解决方案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}}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-17
    • 1970-01-01
    • 2012-08-16
    • 2019-07-27
    • 1970-01-01
    • 1970-01-01
    • 2013-07-21
    相关资源
    最近更新 更多