Получить все цвета изображения

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 вывод 2 миллионов названий цветов (?) на консоль не очень удобен. Что вы будете делать со всеми этими цветовыми значениями?

Caius Jard 22.12.2020 22:45

Если бы это был код C#, вы могли бы параллельно обрабатывать пиксели. Однако это кажется скорее проблемой решения. Вам действительно нужно видеть каждый пиксель и записывать цвет? Не могли бы вы попробовать некоторые из каждой строки? Какова цель на самом деле?

Prateek Shrivastava 22.12.2020 22:46
stackoverflow.com/questions/19586524/…
Caius Jard 22.12.2020 22:46

@Prateek, что, powershell не C#? :)

Caius Jard 22.12.2020 22:47

«Что вы будете делать со всеми этими цветовыми значениями?» - Я хочу найти цветовую карту/матрицу другого изображения на исходном изображении. «Если бы это был код C#, вы могли бы обрабатывать пиксели параллельно» - powershell - это .net, такой как C#, поэтому у powershell есть параллельная способность. "stackoverflow.com/questions/19586524/…" можете ли вы помочь мне перевести это в powershell?

Danny 22.12.2020 22:53

Stack Overflow — это сайт вопросов и ответов, а не сервис перевода кода. Попробуйте сначала перевести код самостоятельно, а затем приходите к нам, когда застрянете, обязательно покажите нам, что вы пробовали, и создайте минимально воспроизводимый пример.

gunr2171 22.12.2020 22:59

@CaiusJard, или есть другой способ поиска целевого изображения на исходном изображении?

Danny 22.12.2020 23:26
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
8
511
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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}}

Другие вопросы по теме