Подтвердить что ты не робот

Какой эффективный способ определить, полностью ли растровое изображение?

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

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

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

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

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

4b9b3361

Ответ 1

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

// Lock the bitmap 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);

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

Ответ 2

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

Ответ 4

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

private bool AllOneColor(Bitmap bmp)
{
    // Lock the bitmap 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;
}

Ответ 5

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

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

Ответ 6

Использование библиотеки 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

Ответ 7

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

Ответ 8

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

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

Ответ 9

У меня есть идея, что вне коробки.

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

EDIT: Я сомневаюсь, что это будет быстрее, чем метод @leonard. Единственная причина, по которой это может быть, если исходный файл не был растровым, но был сжатым форматом изображения. Таким образом, алгоритм контрольной суммы CRC не должен был бы распаковать изображение перед запуском.

Ответ 10

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

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

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

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

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

Ответ 11

    private bool AllOneColor(Bitmap bmp)
    {
        BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
        byte[] rgbValues = new byte[bmpData.Stride * bmpData.Height];
        System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, rgbValues, 0, rgbValues.Length);
        bmp.UnlockBits(bmpData);
        return !rgbValues.Where((v, i) => i % bmpData.Stride < bmp.Width && v != rgbValues[0]).Any();
    }

Ответ 12

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