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
может тут поможет, но если да, то не знаю как это написать...
Если бы это был код C#, вы могли бы параллельно обрабатывать пиксели. Однако это кажется скорее проблемой решения. Вам действительно нужно видеть каждый пиксель и записывать цвет? Не могли бы вы попробовать некоторые из каждой строки? Какова цель на самом деле?
@Prateek, что, powershell не C#? :)
«Что вы будете делать со всеми этими цветовыми значениями?» - Я хочу найти цветовую карту/матрицу другого изображения на исходном изображении. «Если бы это был код C#, вы могли бы обрабатывать пиксели параллельно» - powershell - это .net, такой как C#, поэтому у powershell есть параллельная способность. "stackoverflow.com/questions/19586524/…" можете ли вы помочь мне перевести это в powershell?
Stack Overflow — это сайт вопросов и ответов, а не сервис перевода кода. Попробуйте сначала перевести код самостоятельно, а затем приходите к нам, когда застрянете, обязательно покажите нам, что вы пробовали, и создайте минимально воспроизводимый пример.
@CaiusJard, или есть другой способ поиска целевого изображения на исходном изображении?
Конечно, как codeproject.com/Articles/38619/… или codereview.stackexchange.com/questions/138011/…
Вы действительно можете использовать 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}}
GetPixel медленный, потому что он блокирует и разблокирует каждый пиксель по очереди, и блокировка группы пикселей будет быстрее, но вам действительно нужно лучше указать, каким будет результат этого на самом деле. В изображении 1920x1080 вывод 2 миллионов названий цветов (?) на консоль не очень удобен. Что вы будете делать со всеми этими цветовыми значениями?