Страницы

Поиск по вопросам

воскресенье, 26 января 2020 г.

Перекрытие изображений Bitmap

#c_sharp #net #bitmap


Есть два Bitmap'а с произвольными изображениями на белом фоне. Нужно узнать, перекрываются
ли эти изображения?

Вариант решения - сравнивать изображения по пикселям. Если пиксели с одинаковыми
"координатами" заполнены на обоих рисунках, то изображения перекрываются.

    static bool IsBitmapsIntersect(Bitmap bitmap1, Bitmap bitmap2)
    {
        for (var i = 0; i < Math.Min(bitmap1.Width, bitmap2.Width); i++)
        {
            for (var j = 0; j < Math.Min(bitmap1.Height, bitmap2.Height); j++)
            {
                if (bitmap1.GetPixel(i, j).ToArgb() != -1 && bitmap2.GetPixel(i,
j).ToArgb() != -1)
                {
                    return true;
                }
            }
        }

        return false;
    }


Есть ли какие-нибудь более быстрые/правильные решения этой задачи?

UPD

Воспользовался советом @Discord.
В итоге сделал обертку над Bitmap и GetPixel:

class BitmapInfo
{
    private readonly byte[] _argbBytes;

    private readonly int _pixelSize;

    private readonly int _stride;

    private readonly int _width;

    private readonly int _height;

    public BitmapInfo(Bitmap bitmap)
    {
        var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly, bitmap.PixelFormat);

        _stride = Math.Abs(bitmapData.Stride);
        _argbBytes = new byte[_stride * bitmapData.Height];
        Marshal.Copy(bitmapData.Scan0, _argbBytes, 0, _argbBytes.Length);

        bitmap.UnlockBits(bitmapData);

        _width = bitmap.Width;
        _height = bitmap.Height;

        _pixelSize = Image.GetPixelFormatSize(bitmap.PixelFormat) / 8;
    }

    public int Width
    {
        get { return _width; }
    }

    public int Height
    {
        get { return _height; }
    }

    public int GetPixel(int x, int y)
    {
        var startIndex = x * _stride + y * _pixelSize;
        int result = 0;

        for (var i = _pixelSize - 1; i >= 0; i--)
        {
            result <<= 8;
            result |= _argbBytes[startIndex + i];
        }

        return result;
    }
}

    static bool IsBitmapsIntersect2(Bitmap bitmap1, Bitmap bitmap2)
    {
        var bitmapInfo1 = new BitmapInfo(bitmap1);
        var bitmapInfo2 = new BitmapInfo(bitmap2);

        for (var i = 0; i < Math.Min(bitmapInfo1.Width, bitmapInfo2.Width); i++)
        {
            for (var j = 0; j < Math.Min(bitmapInfo1.Height, bitmapInfo2.Height); j++)
            {
                if (bitmapInfo1.GetPixel(i, j) != -1 && bitmapInfo2.GetPixel(i, j) != -1)
                {
                    return true;
                }
            }
        }

        return false;
    }

    


Ответы

Ответ 1



GetPixel — это очень медленная функция. Всегда используйте LockBits и вручную проходитесь по байтам, особенно если обрабатываете большие области. В качестве альтернативы могу предложить сделать битмапы с бинарными масками ваших двух картинок. На третьем битмапе размером min(b1.width, b2.width) x min(b1.height, b2.height) совместить общую область картинок в режиме AND. Потом пройтись по данным с помощью LockBits и проверить, есть ли хоть какое-то пересечение. Будет ли второй способ быстрее — не знаю, нужно мерять. Он может быть быстрее за счёт оптимизаций в железе/оси, потому что совмещение картинок может быть более быстрым. Ещё один вариант — построить границы в виде многоугольников и искать пересечение, соответственно, многоугольников.

Ответ 2



Сравнение с -1 какое-то странное - не понял, чего ты хочешь этим добиться. А по теме - использование GetPixel - это очень медленная операция. Надо делать LockBits, брать Scan0, превращать данные в массив и работать с этим массивом. Ну и не забыть UnlockBits :)

Ответ 3



Вам нужен RectangleF и его метод IntersectsWith.

Комментариев нет:

Отправить комментарий