Какой эффективный способ определить, является ли растровое изображение полностью черным? - PullRequest
21 голосов
/ 01 апреля 2010

Мне интересно, существует ли сверхэффективный способ подтверждения того, что объект Image ссылается на полностью черное изображение, поэтому каждый пиксель в растровом изображении имеет ARGB (255, 0, 0, 0).

Что бы вы порекомендовали? Большинство этих растровых изображений будут иметь размер 1024 x 6000 пикселей (хотя предположить, что они всегда будут такого размера, небезопасно).

Мне это нужно, потому что у нас проблемы с API PrintWindow. Мы находим, что почти в 20% случаев, по крайней мере, некоторая часть изображения будет черным квадратом (последующий захват будет успешным). Моя идея обойти эту проблему состояла в том, чтобы вызвать PrintWindow или WM_PRINT с каждым дочерним окном, а затем собрать воедино все изображение окна. Если мне удастся найти эффективный способ определения того, что PrintWindow вернул черное изображение для определенного дочернего окна, тогда я смогу снова быстро вызвать PrintWindow для этого захвата. Это отстой, но PrintWindow - единственный метод захвата окна, который работает во всех окнах (в любом случае, я хочу) и поддерживает захват скрытых и / или неэкранных окон.

При сбое PrintWindow оно не устанавливает код ошибки и не возвращает ничего, что указывает на сбой. Когда возникает проблема с черным квадратом, это всегда все окно или дочернее окно, которое возвращает черный цвет. Таким образом, захватывая каждое дочернее окно отдельно, я могу быть уверен, что каждый из моих снимков будет работать, при условии, что он содержит хотя бы один не черный пиксель.

PrintWindow лучше в Vista и выше, по-видимому, но в этом случае мы ограничены Server 2003.

Ответы [ 12 ]

20 голосов
/ 01 апреля 2010

Я бы порекомендовал вам заблокировать растровое изображение в памяти, используя метод LockBits типа System.Drawing.Bitmap. Этот метод возвращает тип BitmapData, из которого вы можете получить указатель на заблокированную область памяти. Затем выполните итерацию в памяти, ища ненулевые байты (действительно, быстрее, сканируя значения Int32 или даже Int64, в зависимости от используемой платформы). Код будет выглядеть так:

// Lock the bitmap's bits.  
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData =bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);

// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;

// Declare an array to hold the bytes of the bitmap.
int bytes  = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];

// Copy the RGB values into the array.
Marshal.Copy(ptr, rgbValues, 0, bytes);

// Scanning for non-zero bytes
bool allBlack = true;
for (int index = 0; index < rgbValues.Length; index++)
    if (rgbValues[index] != 0) 
    {
       allBlack = false;
       break;
    }
// Unlock the bits.
bmp.UnlockBits(bmpData);

Для повышения производительности рассмотрите возможность использования небезопасного кода и прямого доступа к памяти (с помощью указателей).

7 голосов
/ 05 июня 2014

Первый ответ на этот пост - Awesome. Я изменил код для более общего определения того, является ли изображение одним цветом (полностью черным, полностью белым, пурпурным и т. Д.). Предполагая, что у вас есть растровое изображение с 4-мя значениями цвета ARGB, сравните каждый цвет с цветом в верхнем левом углу, если любой отличается, тогда изображение не все одного цвета.

private bool AllOneColor(Bitmap bmp)
{
    // Lock the bitmap's bits.  
    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);

    // Get the address of the first line.
    IntPtr ptr = bmpData.Scan0;

    // Declare an array to hold the bytes of the bitmap.
    int bytes = bmpData.Stride * bmp.Height;
    byte[] rgbValues = new byte[bytes];

    // Copy the RGB values into the array.

    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);

    bool AllOneColor = true;
    for (int index = 0; index < rgbValues.Length; index++)
    {
        //compare the current A or R or G or B with the A or R or G or B at position 0,0.
        if (rgbValues[index] != rgbValues[index % 4])
        {
            AllOneColor= false;
            break;
        }
    }
    // Unlock the bits.
    bmp.UnlockBits(bmpData);
    return AllOneColor;
}
7 голосов
/ 01 апреля 2010

Если бы вы знали больше об условиях, при которых изображение было бы не черным, это было бы проще. Например, как выглядят края или центр изображения, когда оно не черное. По сути, то, что вы создаете, является эвристическим, чтобы угадать не черное изображение и выбрать те области, которые дадут вам самое быстрое прочтение. Если ваша эвристика указывает на полностью черное изображение, то вы можете либо решить, что оно полностью черное, либо выполнить полную проверку всех пикселей. Это сильно зависит от ваших изображений, хотя. Если вам необходимо различать полностью черное изображение и изображение, содержащее один не черный пиксель в произвольном месте, вам придется проверить их все.

6 голосов
/ 01 апреля 2010

Заблокировать растровое изображение в памяти и сканировать его с помощью побитовых операций. Не используйте GetPixel и т.п .; это медленно.

3 голосов
/ 01 апреля 2010

Использование библиотеки AForgeNET (http://www.aforgenet.com) также может быть решением:

public bool IsNotBlackImage()
{
    Assembly assembly = this.GetType().Assembly;
    var imgTest = new Bitmap(assembly.GetManifestResourceStream("TestImage.png"));
    var imgStatistics = new ImageStatistics(imgTest);             
    return imgStatistics.PixelsCountWithoutBlack != 0;
}

Для ссылки на класс ImageStatistics AForge.Imaging.dll в вашем проекте.

http://code.google.com/p/aforge/source/browse/trunk/Sources/Imaging/ImageStatistics.cs

2 голосов
/ 01 апреля 2010

Нарисуйте растровое изображение с ColorMatrix, который имеет 3 x 255 по диагонали, который превратит любой не черный пиксель в чистый белый. Затем нарисуйте это растровое изображение на меньшее, ширина которого кратна 4 и имеет формат Format24bppRgb. Это исключает альфу, уменьшает размер и оставляет только нули, если растровое изображение действительно черное.

Вам придется поэкспериментировать, чтобы увидеть, насколько маленьким вы можете сделать растровое изображение, используйте образец, который имеет только один белый пиксель, чтобы увидеть, когда интерполятор заставит его исчезнуть. Я предполагаю, что вы можете пойти довольно далеко.

1 голос
/ 01 апреля 2010

Достаточно надежным методом будет проверка размера файла изображения. То есть, если не все изображения черного цвета имеют относительно нормальное распределение цветов.

Если вы знаете тип файла, вы знаете некоторые основные сведения о средних коэффициентах сжатия. И вы можете легко определить размеры файла, не переключаясь по всему файлу.

Все черное изображение любого размера, использующее сжатый формат файла, будет иметь очень маленький размер файла по сравнению с изображением идентичных размеров с довольно нормальным распределением цветов.

Этот метод займет немного времени, чтобы протестировать и создать базу знаний о том, какой размер файла полностью черного изображения следует сравнивать с не полностью черным изображением, но это будет очень быстро .

Если у вас есть много случаев, когда не совсем черные изображения довольно близки к полностью черным, очевидно, что этот метод не будет работать.

1 голос
/ 01 апреля 2010

У меня есть идея, которая выходит за рамки.

А как насчет контрольной суммы CRC ? Сначала вы можете проверить размеры изображения, затем рассчитать контрольную сумму и сравнить ее с известными (предварительно рассчитанными) контрольными суммами полностью черного изображения с такими же размерами.

РЕДАКТИРОВАТЬ: Я сомневаюсь, что это будет быстрее, чем метод @ Леонарда. Единственная причина, по которой это возможно, заключается в том, что исходный файл не был растровым, а представлял собой сжатый формат изображения. Таким образом, алгоритм контрольной суммы CRC не будет распаковывать изображение перед запуском.

1 голос
/ 01 апреля 2010

Просто несколько случайных мыслей:

  • Может быть, вы могли бы применить ColorMatrix к оригиналу растровое изображение (чтобы полностью включить его черный). Затем сравните результат с оригинал.
  • Или создайте растровое изображение одинакового размера (заполненный чисто черным), а затем сравнить с исходным растровым изображением.
1 голос
/ 01 апреля 2010

Чтобы быть полностью уверенным в черноте изображения, вам придется проверять каждый пиксель, и доступ к данным пикселя в небезопасном блоке, вероятно, является самым быстрым способом сделать это. Конечно, можно оптимизировать для не черного случая и попытаться найти его раньше, но в худшем случае вам всегда придется проверять каждый пиксель.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...