1

I'm using this code to calculate the average color of an image but this is not working as desired. The result is inaccurate. How to find the average color of the image ?

Private Sub btnAvg_Click() Handles btnAvg.Click
    Dim x, y As Integer
    bmp = New Bitmap(picBox.Tag.ToString)

    For y = picBox.Top To picBox.Bottom
        For x = picBox.Left To picBox.Right
            With bmp.GetPixel(x, y)
                totalR = totalR + .R : totalG = totalG + .G : totalB = totalB + .B
            End With
        Next x
    Next y

    avgR = totalR / ((picBox.Bottom - picBox.Top) * (picBox.Right - picBox.Left))
    avgG = totalG / ((picBox.Bottom - picBox.Top) * (picBox.Right - picBox.Left))
    avgB = totalB / ((picBox.Bottom - picBox.Top) * (picBox.Right - picBox.Left))
End Sub

I'm looking for a hi-speed, more than 80% accurate result. This is not a duplicate question as other questions deals with C# only

Sourav
  • 17,065
  • 35
  • 101
  • 159
  • 2
    `This is not a duplicate question as other questions deals with C# only` - I wouldn't really say that this *excuses* you. – Darin Dimitrov Jul 30 '12 at 15:48
  • `GetPixel` is crazy slow. Lock the bitmap and read the returned byte array. http://msdn.microsoft.com/en-us/library/5ey6h79d.aspx – asawyer Jul 30 '12 at 15:50
  • @asawyer I can overcome the problem with Thumbnail too :P – Sourav Jul 30 '12 at 15:52
  • @DarinDimitrov Yup you are right :D But I've tried code converter too ! – Sourav Jul 30 '12 at 15:53
  • What is the problem you are having with the code that you posted? Is it too slow, or is the result inaccurate? Your specific question is unclear. – Steven Doggart Jul 30 '12 at 15:53
  • @Sourav We aren't Raymond Chen, and do not posses physic debugging skills. – asawyer Jul 30 '12 at 15:55
  • @Sourav Have you considered the possibility that the result is "inaccurate" (how is this measured??) because you claim to be reading a compressed thumbnail, not the actual image? – asawyer Jul 30 '12 at 16:00
  • @asawyer I want to get average skin color of a human in passport size photo but this function returns much deeper color :( – Sourav Jul 30 '12 at 16:02
  • You are not initializing the total variables with 0. So, when you click the button repeatedly, you will get brighter and brighter colors. – Olivier Jacot-Descombes Aug 13 '22 at 15:36

3 Answers3

4

This should work:

Private Function getAverageColor(ByVal imageFilePath As String) As Color
    Dim bmp As New Bitmap(imageFilePath)
    Dim totalR As Integer = 0
    Dim totalG As Integer = 0
    Dim totalB As Integer = 0
    For x As Integer = 0 To bmp.Width - 1
        For y As Integer = 0 To bmp.Height - 1
            Dim pixel As Color = bmp.GetPixel(x, y)
            totalR += pixel.R
            totalG += pixel.G
            totalB += pixel.B
        Next
    Next
    Dim totalPixels As Integer = bmp.Height * bmp.Width
    Dim averageR As Integer = totalR \ totalPixels
    Dim averageg As Integer = totalG \ totalPixels
    Dim averageb As Integer = totalB \ totalPixels
    Return Color.FromArgb(averageR, averageg, averageb)
End Function
Steven Doggart
  • 43,358
  • 8
  • 68
  • 105
  • Are you saying this doesn't work, or are you saying that it does work and you don't understand why? – Steven Doggart Jul 30 '12 at 16:35
  • 1. I think your code and my code are same. 2. The result is not desired. _Perhaps the problem is with the algo, finding average of hot pink and navy yellow_ – Sourav Jul 30 '12 at 16:39
  • Agreed, this is following the premise that you actually want the average R, G, and B of all the pixels. If that is not what you want, then none of this is a solution. However, the main difference between your code and mine is that mine loops through from 0 to the height and width of the bitmap, rather than the top and bottom of the PictureBox control. Using the position and size of the picture box will not work properly unless its location is 0, 0 and the size matches the bitmap exactly. – Steven Doggart Jul 30 '12 at 17:30
  • You also initialize the total variables with 0, what the OP omitted. – Olivier Jacot-Descombes Aug 13 '22 at 15:38
3

Averaging colors doesn't make much sense in general. What's the average of hot pink and navy yellow? The way you are calculating it now produces a completely different color.

You'll need to work in a different color space. HSB is an easy one to work with and directly supported by the Color type. Albeit that it doesn't deal with the strongly non-linear color perception of the human eye. Color.GetHue() returns the hue, a value that's directly proportional to the color. You also ought to create a histogram so that the background of the photo doesn't disturb the results too much. Or at least work from the center of photo outwards. Whether that's good enough to detect skin color is fairly doubtful.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • +1 for giving some idea, but please advice how to solve the problem [_some algo or code_] :P ! – Sourav Jul 30 '12 at 16:27
  • Sorry, I can't do your job for you. This would not be a project I would have taken on, image processing like this is notoriously difficult. – Hans Passant Jul 30 '12 at 16:44
  • 1
    Th question need to more thoughts... As Hans said, the average does not make sense in imaging. This would assume the whole image is equaly important. In a portrait, the important thig is the face, not the background! So you need to specify why you need an average? For example, reduce quality, add or manage effects on image, compressing, or even ordering images all work on some part of average, but each got specific algorithms. Those algos make sense of average. Also look at this : (http://stackoverflow.com/questions/5050250/fast-way-of-getting-the-dominant-color-of-an-image) – Minus Jul 30 '12 at 18:08
  • Yes, averaging the R, G, Bs has the tendency to produce gray colors, whereas averaging the hue would preserve the average saturation. But one must take care with the hue as it is defined as an angle and the average of say 355° and 9° should be 2° and not 182°! I am not sure how the average of more than 2 hues should be calculated. – Olivier Jacot-Descombes Aug 13 '22 at 15:50
0

You could try this code and see if the results suit you. It reduces the image to a single pixel thumbnail:

Private Function getAverageColor(ByVal imageFilePath As String) As Color
    Dim bmp As New Bitmap(imageFilePath)
    Dim thumb As New Bitmap(bmp.GetThumbnailImage(1, 1, Nothing, IntPtr.Zero))
    Return thumb.GetPixel(0, 0)
End Function
Costas
  • 171
  • 2
  • 4