C#

时间:2018-12-23 18:27:11

标签: c# image bitmap

我遇到了以下来自here的代码:

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库-例如,请参见:

enter link description here

PS:

我开始使用LockBits重写以上内容。下面的代码是我当前的尝试,但是我有点卡住了。请注意,bmp1是“目标图片”,并且不会真正改变-因此可以将复制排除在外/只需执行一次即可。将位图bmp2传入并与bmp1进行比较(有100个bmp2s)。最终,我想使用某个距离(例如字节的欧几里得距离?)来确定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;
    }
  }
}

2 个答案:

答案 0 :(得分:1)

始终使用所有像素将非常耗时。如果使用随机选择的图像像素样本怎么办。另外,您可以应用分层图像粒度。这样,您将获得有关图像中显示的详细信息的更多信息。

我也在从事类似的项目。在GitHub上可以使用Ellipses-Image-Approximator的名称。

类似这样的东西:

package eu.veldsoft.ellipses.image.approximator;

import java.awt.image.BufferedImage;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

/**
 * Compare to raster images by using Euclidean distance between the pixels but
 * in probabilistic sampling on hierarchical image detailization.
 * 
 * @author Todor Balabanov
 */
class HierarchicalProbabilisticImageComparator implements ImageComparator {
    /** A pseudo-random number generator instance. */
    private static final Random PRNG = new Random();

    /**
     * Euclidean distance color comparator instance.
     */
    private static final ColorComparator EUCLIDEAN = new EuclideanColorComparator();

    /** Recursive descent depth level. */
    private int depthLevel = 1;

    /**
     * Size of the sample in percentages from the size of the population (from
     * 0.0 to 1.0).
     */
    private double samplePercent = 0.1;

    /** A supportive array for the first image pixels. */
    private int aPixels[] = null;

    /** A supportive array for the second image pixels. */
    private int bPixels[] = null;

    /**
     * Constructor without parameters for default members' values.
     */
    public HierarchicalProbabilisticImageComparator() {
        this(1, 0.1);
    }

    /**
     * Constructor with all parameters.
     * 
     * @param depthLevel
     *            Recursive descent depth level.
     * @param samplePercent
     *            Size of the sample in percentages from the size of the
     *            population (from 0.0 to 1.0).
     */
    public HierarchicalProbabilisticImageComparator(int depthLevel,
            double samplePercent) {
        super();

        this.depthLevel = depthLevel;
        this.samplePercent = samplePercent;
    }

    private double distance(int width, int level, int minX, int minY, int maxX,
            int maxY) {
        /*
         * At the bottom of the recursive descent, distance is zero, and
         * descending is canceled.
         */
        if (level > depthLevel) {
            return 0;
        }

        /* Rectangle's boundaries should be observed. */
        if (maxX <= minX || maxY <= minY) {
            return 0;
        }

        /*
         * Sample size calculated according formula.
         * 
         * https://www.surveymonkey.com/mp/sample-size-calculator/
         */
        int sampleSize = (int) ((maxX - minX) * (maxY - minY) * samplePercent);

        /* Generate unique indices of pixels with the size of the sample. */
        Set<Integer> indices = new HashSet<Integer>();
        while (indices.size() < sampleSize) {
            int x = minX + PRNG.nextInt(maxX - minX + 1);
            int y = minY + PRNG.nextInt(maxY - minY + 1);
            indices.add(y * width + x);
        }

        /* The Euclidean distance of the randomly selected pixels. */
        double sum = 0;
        for (int index : indices) {
            sum += EUCLIDEAN.distance(aPixels[index], bPixels[index]);
        }

        /* Do a recursive descent. */
        return (sum / sampleSize) * level
                + distance(width, level + 1, minX, minY,
                        maxX - (maxX - minX) / 2, maxY - (maxY - minY) / 2)
                + distance(width, level + 1, maxX - (maxX - minX) / 2, minY,
                        maxX, maxY - (maxY - minY) / 2)
                + distance(width, level + 1, minX, maxY - (maxY - minY) / 2,
                        maxX - (maxX - minX) / 2, maxY)
                + distance(width, level + 1, maxX - (maxX - minX) / 2,
                        maxY - (maxY - minY) / 2, maxX, maxY);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double distance(BufferedImage a, BufferedImage b) {
        if (a.getWidth() != b.getWidth()) {
            throw new RuntimeException("Images width should be identical!");
        }

        if (a.getHeight() != b.getHeight()) {
            throw new RuntimeException("Images height should be identical!");
        }

        aPixels = a.getRGB(0, 0, a.getWidth(), a.getHeight(), null, 0,
                a.getWidth());

        bPixels = b.getRGB(0, 0, b.getWidth(), b.getHeight(), null, 0,
                b.getWidth());

        /* Do a recursive calculation. */
        return distance(Math.min(a.getWidth(), b.getWidth()), 1, 0, 0,
                Math.min(a.getWidth() - 1, b.getWidth() - 1),
                Math.min(a.getHeight() - 1, b.getHeight() - 1));
    }
}

答案 1 :(得分:0)

正如其他人指出的那样,您可以使用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;
}