Getting the total image value

In a previous question that I asked: Get image value total, I learned to use LockBits to speed up my code execution's time. Since then I have changed my code to Ham the distance instead of just getting the total value of the image. Here is my code:

private double GetImageValue(Bitmap Image, Size ImageSize)
{
double ImageValue = 0;
List<double> Location = new List<double>();

Image = new Bitmap(Image, ImageSize);

for (int X = 0; X < Image.Width; X++)
{
for (int Y = 0; Y < Image.Height; Y++)
{
Color PixelColor = Image.GetPixel(X, Y);
int PixelValue = PixelColor.A + PixelColor.B + PixelColor.G + PixelColor.R;

while (Location.Count <= PixelValue)
{
}
Location[PixelValue] += 1;
}
}
for (int i = 0; i < Location.Count; i++)
{
ImageValue += i + Location[i];
}
return ImageValue;
}


I do not know how to use LockBits to get the image value. Is it possible to use LockBits to get the image value? Thanks for any help.

Just to give you some additional suggestions.

this part :

while (Location.Count <= PixelValue)
{
}


can be replaced with this :

if (Location.Count <= PixelValue)
{
Location.AddRange(new double[PixelValue - Location.Count + 1]);
}


for this part :

for (int i = 0; i < Location.Count; i++)
{
ImageValue += i + Location[i];
}


you can totally replaced it with simple calculation. So, what you need is just to know is the max value of pixelValue along with the total count of repeated pixelValue to be added to the calculation.

So, if you have a serious of numbers to be cumulated like :

1+2+3+4+5+6+7+...etc


you can take the minimum and the maximum numbers, add them together, then divide them by 2 then multiply the result by the maximum number. This would give you the final result directly.

So, you would end-up doing something like this :

var sumTotalCount = Location.Sum(); // sum all the values

ImageValue =  (maxPixelValue * ((maxPixelValue + 1) / 2)) + (sumTotalCount) + Location.Count;


doing this, would also give you advantage to avoid using List<double> as well, and use variables to store the total pixelValue count and the max value of pixelValue.

Example :

var counter = 0;
var maxValue = 0;

for (int y = 0, offset = 0; y < image.Height; y++, offset += bmpData.Stride)
{
for (int x = 0, index = offset; x < image.Width; x++, index += 4)
{

byte blue = values[index];
byte green = values[index + 1];
byte red = values[index + 2];
byte alpha = values[index + 3];

Color color = Color.FromArgb(alpha, red, green, blue);

int pixelValue = (color.A + color.B + color.G + color.R) - 1;

if(pixelValue > maxValue)
{
maxValue = pixelValue;
}

counter++;
}
}

var imageValue = (maxValue * ((maxValue + 1) / 2)) + counter + maxValue + 1;


I haven't test it, but theoretically should return the same result.

The following helper class should be useful if you need to compare images:

class LockedBitmap : IDisposable
{
public LockedBitmap(Bitmap image)
{
bmp = image;
rect = new Rectangle(0, 0, image.Width, image.Height);
stride = Math.Abs(bmpData.Stride);
var ptr = bmpData.Scan0;
var bytes = stride * image.Height;
data = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, data, 0, bytes);
}

public Color GetPixel(int x, int y)
{
int offset = y * stride + x * 3;
int r = data[offset + 2];
int g = data[offset + 1];
int b = data[offset + 0];
return Color.FromArgb(r, g, b);
}

public void SetPixel(int x, int y, Color col)
{
int offset = y * stride + x * 3;
data[offset + 2] = col.R;
data[offset + 1] = col.G;
data[offset + 0] = col.B;
}

public int Stride { get { return stride; } }

public void Dispose()
{
bmp.UnlockBits(bmpData);
}

public byte[] data;

private int stride;
private Bitmap bmp;
private BitmapData bmpData;
private Rectangle rect;
}


This can be used like so:

long HamImages(Bitmap img1, Bitmap img2)
{
if (img1.Width != img2.Width || img1.Height != img2.Height)
return long.MaxValue;

using var lbm1 = new LockedBitmap(img1);
using var lbm2 = new LockedBitmap(img2);

long total = 0;
for (int y = 0; y<img1.Height; y++)
{
for (int x = 0; x < img1.Width; x++)
{
Color col1 = lbm1.GetPixel(x, y);
Color col2 = lbm2.GetPixel(x, y);
total += Math.Abs(col1.R - col2.R) + Math.Abs(col1.G - col2.G) + Math.Abs(col1.B - col2.B);
}
}
}


The problem with this is that you are missing out on some of the benefits of locking the bitmap in the first case. It would be more efficient to work on the raw values than converting them to a Color:

long HamImages(Bitmap img1, Bitmap img2)
{
if (img1.Width != img2.Width || img1.Height != img2.Height)
return long.MaxValue;

using var lbm1 = new LockedBitmap(img1);
using var lbm2 = new LockedBitmap(img2);
long total = 0;
for (int y = 0; y<img1.Height; y++)
{
int offset1 = y * lbm1.Stride;
int offset2 = y * lbm2.Stride;
for (int x = 0; x < img1.Width; x++)
{
int r1 = lbm1.data[offset1 + x * 3 + 2];
int g1 = lbm1.data[offset1 + x * 3 + 1];
int b1 = lbm1.data[offset1 + x * 3 + 0];

int r2 = lbm2.data[offset2 + x * 3 + 2];
int g2 = lbm2.data[offset2 + x * 3 + 1];
int b2 = lbm2.data[offset2 + x * 3 + 0];

total += Math.Abs(r1 - r2) + Math.Abs(g1 - g2) + Math.Abs(b1 - b2);
}
}