расстояние / сходство между 2 растровыми изображениями в C # - PullRequest
0 голосов
/ 23 декабря 2018

Я наткнулся на следующий код, взятый из здесь :

using System;
using System.Drawing;

class Program
{
    static void Main()
    {
        Bitmap img1 = new Bitmap("Lenna50.jpg");
        Bitmap img2 = new Bitmap("Lenna100.jpg");

        if (img1.Size != img2.Size)
        {
            Console.Error.WriteLine("Images are of different sizes");
            return;
        }

        float diff = 0;

        for (int y = 0; y < img1.Height; y++)
        {
            for (int x = 0; x < img1.Width; x++)
            {
                diff += (float)Math.Abs(img1.GetPixel(x, y).R - img2.GetPixel(x, y).R) / 255;
                diff += (float)Math.Abs(img1.GetPixel(x, y).G - img2.GetPixel(x, y).G) / 255;
                diff += (float)Math.Abs(img1.GetPixel(x, y).B - img2.GetPixel(x, y).B) / 255;
            }
        }

        Console.WriteLine("diff: {0} %", 100 * diff / (img1.Width * img1.Height * 3));
    }
}

К сожалению, это действительно медленно.Кто-нибудь знает о более быстром способе вычисления расстояния между двумя изображениями?Спасибо!

Чтобы предоставить больше контекста.Я работаю над чем-то вроде этого:

https://rogerjohansson.blog/2008/12/07/genetic-programming-evolution-of-mona-lisa/

Я развиваю SVG, которые затем переводятся в растровое изображение и сравниваются с целевым изображением.

Просто наткнулся наБиблиотека aforgenet - см., например:

введите описание ссылки здесь

PS:

Я начинаю переписывать вышеупомянутое с помощью LockBits.Код ниже - моя текущая попытка, но я немного застрял.Обратите внимание, что bmp1 является «целевой картинкой» и на самом деле не изменяется - следовательно, копирование может быть разобрано / нужно выполнить только один раз.Растровое изображение bmp2 передается и сравнивается с bmp1 (существует 100 с bmp2).В конечном счете, я хотел бы определить сходство между bmp1 и bmp2, используя некоторое расстояние (например, евклидово расстояние в байтах?).Любые указатели относительно этого и как ускорить код был бы очень признателен.Спасибо!

public double Compare(Bitmap bmp1, Bitmap bmp2)
{
    BitmapData bitmapData1 = bmp1.LockBits(new Rectangle(0, 0, bmp1.Width, bmp1.Height), ImageLockMode.ReadWrite, bmp1.PixelFormat);
    BitmapData bitmapData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), ImageLockMode.ReadWrite, bmp2.PixelFormat);

    IntPtr ptr1 = bitmapData1.Scan0;
    int bytes1 = bitmapData1.Stride * bmp1.Height;
    byte[] rgbValues1 = new byte[bytes1];
    byte[] r1 = new byte[bytes1 / 3];
    byte[] g1 = new byte[bytes1 / 3];
    byte[] b1 = new byte[bytes1 / 3];
    Marshal.Copy(ptr1, rgbValues1, 0, bytes1);
    bmp1.UnlockBits(bitmapData1);

    IntPtr ptr2 = bitmapData2.Scan0;
    int bytes2 = bitmapData2.Stride * bmp2.Height;
    byte[] rgbValues2 = new byte[bytes2];
    byte[] r2 = new byte[bytes2 / 3];
    byte[] g2 = new byte[bytes2 / 3];
    byte[] b2 = new byte[bytes2 / 3];
    Marshal.Copy(ptr2, rgbValues2, 0, bytes2);
    bmp2.UnlockBits(bitmapData2);

    int stride = bitmapData1.Stride;
    for (int column = 0; column < bitmapData1.Height; column++)
    {
        for (int row = 0; row < bitmapData1.Width; row++)
        {
            //????      
        }
    }

    return 0;
}

PPS:

Я (кажется, я) добился еще большего прогресса.Кажется, работает следующий код:

using System.Drawing;
using System.Drawing.Imaging;
using Color = System.Drawing.Color;

namespace ClassLibrary1
{
  public unsafe class BitmapComparer : IBitmapComparer
  {
    private readonly Color[] _targetBitmapColors;
    private readonly int _width;
    private readonly int _height;
    private readonly int _maxPointerLength;
    private readonly PixelFormat _pixelFormat;

    public BitmapComparer(Bitmap targetBitmap)
    {
      _width = targetBitmap.Width;
      _height = targetBitmap.Height;
      _maxPointerLength = _width * _height;
      _pixelFormat = targetBitmap.PixelFormat;
      _targetBitmapColors = new Color[_maxPointerLength];

      var bData = targetBitmap.LockBits(new Rectangle(0, 0, _width, _height), ImageLockMode.ReadWrite, _pixelFormat);
      var scan0 = (byte*) bData.Scan0.ToPointer();

      for (var i = 0; i < _maxPointerLength; i += 4)
      {
        _targetBitmapColors[i] = Color.FromArgb(scan0[i + 2], scan0[i + 1], scan0[i + 0]);
      }

      targetBitmap.UnlockBits(bData);
    }

    // https://rogerjohansson.blog/2008/12/09/genetic-programming-mona-lisa-faq/
    public double Compare(Bitmap bitmapToCompareWith)
    {
      var bData = bitmapToCompareWith.LockBits(new Rectangle(0, 0, _width, _height), ImageLockMode.ReadWrite, _pixelFormat);
      var scan0 = (byte*) bData.Scan0.ToPointer();
      double distance = 0;

      for (var i = 0; i < _maxPointerLength; i += 4)
      {
        distance += 
                ( ((_targetBitmapColors[i].R - scan0[i + 2]) * (_targetBitmapColors[i].R - scan0[i + 2]))
                + ((_targetBitmapColors[i].G - scan0[i + 1]) * (_targetBitmapColors[i].G - scan0[i + 1]))
                + ((_targetBitmapColors[i].B - scan0[i + 0]) * (_targetBitmapColors[i].B - scan0[i + 0])));
      }

      bitmapToCompareWith.UnlockBits(bData);

      return distance;
    }
  }
}

1 Ответ

0 голосов
/ 29 декабря 2018

Как уже отмечали другие, вы можете использовать BitMap.LockBits и использовать указатели вместо GetPixel.Следующее выполняется примерно в 200 раз быстрее, чем оригинальный подход:

static float CalculateDifference(Bitmap bitmap1, Bitmap bitmap2)
{
    if (bitmap1.Size != bitmap2.Size)
    {
        return -1;
    }

    var rectangle = new Rectangle(0, 0, bitmap1.Width, bitmap1.Height);

    BitmapData bitmapData1 = bitmap1.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap1.PixelFormat);
    BitmapData bitmapData2 = bitmap2.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap2.PixelFormat);

    float diff = 0;
    var byteCount = rectangle.Width * rectangle.Height * 3;

    unsafe
    {
        // scan to first byte in bitmaps
        byte* pointer1 = (byte*)bitmapData1.Scan0.ToPointer();
        byte* pointer2 = (byte*)bitmapData2.Scan0.ToPointer();

        for (int x = 0; x < byteCount; x++)
        {
            diff += (float)Math.Abs(*pointer1 - *pointer2) / 255;
            pointer1++;
            pointer2++;
        }
    }

    bitmap1.UnlockBits(bitmapData1);
    bitmap2.UnlockBits(bitmapData2);

    return 100 * diff / byteCount;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...