I wrote an extension method for bitmaps which finds the brightest rectangle (with specified width & height) within an image.
Hopefully my code comments eliminate any confusion. If not, please let me know what I need to elaborate on.
I would like some help reducing repetitive calls to the conjoined Bitmap.GetPixel & Color.GetBrightness methods during the scanning process. (subsequently improving performance tremendously)
Method:
public static Rectangle GetBrightestRectangle(this Bitmap bitmap, int width, int height)
{
// Each rectangle's value is its average pixel color.
var rectangles = new Dictionary<Rectangle, float>();
// Iterate through all possible rectangle points.
for (var x = 0; x < bitmap.Width - width; x++)
for (var y = 0; y < bitmap.Height - height; y++)
{
var brightnesses = new List<float>();
// Iterate through all rectangle pixels.
for (var w = x; w < x + width; w++)
for (var h = y; h < y + height; h++)
{
brightnesses.Add(bitmap.GetPixel(w, h).GetBrightness());
}
rectangles.Add(new Rectangle(x, y, width, height), brightnesses.Average());
}
// The brightness ranges from 0.0 through 1.0, where 0.0 represents black and 1.0 represents white.
return rectangles.OrderByDescending(pair => pair.Value).First().Key;
}
Demo:
var bitmap = (Bitmap)Image.FromFile(@"C:\Steve.png");
var stopwatch = Stopwatch.StartNew();
var brightestRectangle = bitmap.GetBrightestRectangle(50, 25);
stopwatch.Stop();
using (var graphics = Graphics.FromImage(bitmap))
{
graphics.DrawRectangle(new Pen(Color.Lime, 5), brightestRectangle);
}
bitmap.Save(@"C:\Jobs.png");
Console.WriteLine($"bitmap.Size: {bitmap.Size}");
Console.WriteLine($"brightestRectangle: {brightestRectangle}");
Console.WriteLine($"stopwatch.Elapsed.Seconds: {stopwatch.Elapsed.Seconds}");
Steve.png
Jobs.png
bitmap.Size: {Width=500, Height=500}
brightestRectangle: {X=194,Y=99,Width=50,Height=25}
stopwatch.Elapsed.Seconds: 37
GetPixel
is really slow because it has to lock a part of the bitmap with each call. The idea is basically to lock the whole once and then manually index into the buffer. (Not that this makes looking for a better algorithm redundant, which will be necessary for the procedure to scale) \$\endgroup\$