[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 695
  • Last Modified:

scan picture for specific hex color values? urgent

I have a list of hex values that I want to check an image for, if there is a high percentage of one of these hex values then I want to display some information if the list of colors are detected?

How can I use an array to loop through my hex values i have stored in a text file and check the picture(picturebox) for any of the hex values in the array or text file and display a msgbox of the most percentage of the found hex value color in my list?
0
nffvrxqgrcfqvvc
Asked:
nffvrxqgrcfqvvc
  • 9
  • 7
1 Solution
 
nffvrxqgrcfqvvcAuthor Commented:
As a starting point i can check for 1 hex value like this...

If Hex(Picture1.Point(x, y)) = Hex(&HC6D7FF) Then
MsgBox "found"
Else
Debug.Print "not found:"
End If

But i want to put all the possible hex values to check in a text file then i supose load those hex values into and array and scan the image for the most percentage of the certain hex value that is found.
0
 
SizeyCommented:
In your code there is no need for the Hex() function at all, as HC6D7FF (without quotes) is already a hex value, and hex values can be compared non-hex numbers (it is just a visual representation,  a = &H10  is exactly the same as  a = 16 )

Assuming that your file has one hex value per line, and the scalemode of your picturebox is pixels, this should work:

Dim filename as String
  filename = App.Path & "\colours.txt"    '(enter your file name & path here if it is different)

Dim sText as String         'Step 1 - Load the file into an array
Dim aColours as Variant
  Open filename For Input As #1
  sText = Input(LOF(1), #1)
  Close #1
  aColours = Split(sText, vbNewLine)   'aColours now contains all of the colours in the file, as text

Dim lIndex as Long          'Step 2 - Convert the text array into a numeric array (makes step 3 significantly faster), and set up counters
Dim aCount() as Long      
Dim lColourCount as Long
  lColourCount = UBound(aColours)
  ReDim aCount(lColourCount) as Long
  For lIndex = 0 To lColourCount
    aColours(lIndex) = CLng(aColours(lIndex))   '(you could use the Hex function here, but CLng is faster)
    aCount(lIndex) = 0
  Next lIndex

Dim lRow as Long           'Step 3 - Count the number of occurences in the picture
Dim lCol as Long
Dim lColour as Long
  With Picture1
    For lRow = 0 to .Picture.Height   '(these two lines may not be quite right - I cant test on this PC)
      For lCol = 0 to .Picture.Width
        For lColour = 0 To lColourCount
           If .Point(lCol, lRow)) = aColour(lColour) Then
             aCount(lColour) = aCount(lColour) + 1
             Exit For  '(move on to next pixel)
           End If
        Next lColour
      Next lCol
    Next lRow
  End With    '(aCount now contains the number of times that each coulour is in the picture)

Dim lMaxCount as Long        'Step 4 - do something with the data (show the colour with the highest count)
Dim lMaxCountPos as Long
  lMaxCountPos = 0
  lMaxCount = aCount(0)
  For lIndex = 1 To lColourCount
    If aCount(lIndex) > lMaxCount Then
       lMaxCountPos = lIndex
       lMaxCount = aCount(lIndex)
    End If
  Next lIndex
  Msgbox "Colour " & Hex(aColour(lMaxCountPos)) & " was found " & lMaxCount & " times"


Note that step 3 could be done a bit faster (if it was made much more complex), but it sounds like this should be ok for your needs.
0
 
SizeyCommented:
edit: step 3 should avoid calling the Point method inside the colour loop, it should be:

Dim lRow as Long           'Step 3 - Count the number of occurences in the picture
Dim lCol as Long
Dim lColour as Long
Dim lPixelColour as Long
  With Picture1
    For lRow = 0 to .Picture.Height   '(these two lines may not be quite right - I cant test on this PC)
      For lCol = 0 to .Picture.Width    '
        lPixelColour = .Point(lCol, lRow)
        For lColour = 0 To lColourCount
           If lPixelColour = aColour(lColour) Then
             aCount(lColour) = aCount(lColour) + 1
             Exit For  '(move on to next pixel)
           End If
        Next lColour
      Next lCol
    Next lRow
  End With    '(aCount now contains the number of times that each coulour is in the picture)
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
nffvrxqgrcfqvvcAuthor Commented:
I get sub or function not defined on the following line:

 If lPixelColour = acolour(lColour) Then
0
 
SizeyCommented:
Oops..  minor typo there (and in step 4), I missed the S in aColours
0
 
nffvrxqgrcfqvvcAuthor Commented:
Yes I i did change all that but i still am getting errors
0
 
nffvrxqgrcfqvvcAuthor Commented:
After changing it to aColours i get an error on the following line::

  aColours(lIndex) = CLng(aColours(lIndex))   '(you could use the Hex function here, but CLng is faster)

TYPE MISMATCH
0
 
nffvrxqgrcfqvvcAuthor Commented:
The type mismatch is this exact line:      CLng(aColours(lIndex))

however acolours = "HEX COLOR" and lIndex=0 and CLng(aColours(lIndex))=type mismatch
when i move the cursor over the debuger it displays this
0
 
SizeyCommented:
it should be able to convert a hex string.. (it works in the VBA editor of Excel)

have you got the text "HEX COLOR" in your file?  if so, you should remove it, and only have the Hex values in your file.
(or do you mean aColours(lIndex) contains something like "&HC6D7FF" ?  in this case it should be fine)


An alternative for that line:
aColours(lIndex) = Val(aColours(lIndex))
0
 
nffvrxqgrcfqvvcAuthor Commented:
I dont actually have the &H i just have thisis the text file

7B96C6
8496C6
7B92BD
6B8AC6
6379B5
637DB5
5271AD
4A6DA5
42699C
426594
396194
314563
294D73


Should I replce all of them with &H00 at the beggining?
0
 
nffvrxqgrcfqvvcAuthor Commented:
I got it to scan but it freezes VB and it makes the IDE not respond I need to CTRL ALT DELETE
0
 
SizeyCommented:
You need the &H (it isn't a Hex number without it), unless you change the code to:

aColours(lIndex) = CLng("&H" & aColours(lIndex))


To stop the "freeze", add a DoEvents like this (in step 3):

       Next lColour
       DoEvents
      Next lCol
      '(or here instead)
    Next lRow

This will slow things down a bit, but your program will respond more quickly.
0
 
nffvrxqgrcfqvvcAuthor Commented:
Aright..basically I will give you these points anyway, but my goal was to check a picture to see if a certain image if the person was either wearing clothing or if they are not wearin clothing..At first I was just going to store the hex values in a text file and check for the hex value itself(these were skin tone like hex values) but then I found out that almost every picture is different and has different hex values...( so that route didn't work)

But I have another IDEA:

Now i changed it so that I am looking at the middle of the image and checking for RGB values like if there is red blue or green in the middle than the person is either wearing a red,blue or green shirt...some images vary depending on the positioning of the person in the image thats wearing the shirt...I can get it to work to a certain extent using this code but if the hex value isnt an absolute value of RED GREEN or BLUE it won't detect it..

This is how I managed to go about it..

If you know the answere I will start a new thread and give you 500 more points:



Private Declare Function GetNearestColor Lib "gdi32" _
(ByVal hdc As Long, ByVal crColor As Long) As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)



Private Sub Form_Load()
Form1.Visible = True
Form1.Show
Picture1.AutoRedraw = True
   Picture1.Picture = LoadPicture("C:\pa7.bmp")
     Picture1.PaintPicture Picture1.Picture, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight


   
    Caption = Hex(Picture1.Point(Picture1.Width / 2, Picture1.Height / 2))
    Label1.BackColor = "&H" & Caption
    On Error Resume Next
   
    Label1.BackColor = GetNearestColor(Picture1.hdc, Caption)


For i = 1 To 255
If Picture1.Point(Picture1.Width / 2, Picture1.Height / 2) = RGB(0, i, 0) Then
MsgBox "WEARING A GREEN SHIRT"

Else

If Picture1.Point(Picture1.Width / 2, Picture1.Height / 2) = RGB(i, 0, 0) Then
MsgBox "WEARING A RED SHIRT"

Else

If Picture1.Point(Picture1.Width / 2, Picture1.Height / 2) = RGB(0, 0, i) Then
MsgBox "WEARING A BLUE SHIRT"

Else

If Picture1.Point(Picture1.Width / 2, Picture1.Height / 2) = vbBlack Then
MsgBox "WEARING A WHITE SHIRT"


End If
End If
End If
End If

Next i
End Sub
0
 
SizeyCommented:
Ah... checking for skin is rather complex to say the least!!

The company I work for was part of a trial for a big project like that, being done by a major email scanning firm; even after months of many people developing it (probably dozens of them) it was still producing more false positives than valid positives - it was stopping company logos coming though, but not stopping several unwanted photos.

You need to take into account that there are many skin colours (just think of how many hundreds of shades of "white" there are, even without skin blemishes and tattoos etc.), and that clothing can be a colour which is very similar to a skin colour (not necesarilly the same as the person wearing it though), and that you will almost certainly have a background too - which could contain sections that are almost "skin" coloured too.

No matter how you try to work it out, the best you can manage to work out is if there are patches of a particular colour (in various similar shades).  Your idea of converting the colour down to RGB is good, but doesn't offer enough reduction for this kind of work (there are still 16 million colours!).

What you need to do really is check each pixel against each of it's neighbours, and see how much change there is in each of R, G, and B - if the change is big enough then treat it as a different colour (not like the method below!).  With a lot more theory work you could detemine how big the patches of the "same" colour are, and apply some sort of rule.


For what you are currently trying though..
To extract the RGB portion of your pixel colour you could use this:

Dim lRed as Long, lGreen as Long, lBlue as Long
  lRed = Picture1.Point(Picture1.Width / 2, Picture1.Height / 2) Mod 256
  lGreen = (Picture1.Point(Picture1.Width / 2, Picture1.Height / 2) \ 256) Mod 256
  lBlue = (Picture1.Point(Picture1.Width / 2, Picture1.Height / 2) \ 256) \ 256

Then you can take a fraction of the values to work out vaguely what colour you have, eg:
  lRed = lRed \ 25.6   '(convert to 0-10)
  lGreen = lGreen \ 25.6
  lBlue = lBlue \ 25.6

  Select Case lRed
  Case > 7   '(lots of red)

    Select Case lGreen
    Case > 7   '(lots of green)
      Select Case lBlue
      Case > 7:    MsgBox "white"
      Case > 3:    MsgBox "light yellow"
      Case Else:   MsgBox "dark yellow"
      End Select
    Case > 3   '(medium green)
      Select Case lBlue
      Case > 7:    MsgBox "pink"
      Case > 3:    MsgBox "light orange"
      Case Else:   MsgBox "orange"
      End Select
    Case Else  '(some/no green)
      Select Case lBlue
      Case > 7:    MsgBox "??"
      Case > 3:    MsgBox "??"
      Case Else:   MsgBox "??"
      End Select
    End Select

  Case > 3   '(medium red)
    '(repeat entire "lGreen" section here, but with different colours)

  Case Else  '(some/no red)
    '(repeat entire "lGreen" section here, but with different colours)

  End Select
0
 
nffvrxqgrcfqvvcAuthor Commented:
Thank you for your comments...

'Check this out....

This is what I am in the process of doing..This sub will flash all pixels that it finds on the image when specifying a specific hex value..


Private Sub FlashColors(PicBox As PictureBox, Pic As String, _
                        ColorToFlashFrom As OLE_COLOR, _
                        ColorToFlashTo As OLE_COLOR, _
                        NoFlashes As Long)
                       
    Dim Black() As Boolean
    Dim PresentColor As OLE_COLOR
    Dim x As Long
    Dim y As Long
    Dim i As Integer
    Dim ii As Integer
    Dim xx As Long
    Dim yy As Long
   
    PicBox.AutoRedraw = True
    PicBox.Picture = LoadPicture(Pic)
    PicBox.PaintPicture PicBox.Picture, 0, 0, PicBox.ScaleWidth, PicBox.ScaleHeight
   
    ReDim Black(PicBox.ScaleWidth / 15, PicBox.ScaleHeight / 15) As Boolean
   
    For x = 0 To PicBox.ScaleWidth / 15
        For y = 0 To PicBox.ScaleHeight / 15
            If PicBox.Point(x * 15, y * 15) = ColorToFlashFrom Then
                Black(x, y) = True
               
            Else
                Black(x, y) = False
            End If
        Next y
        y = 0
    Next x
     
    PresentColor = ColorToFlashTo
    PicBox.AutoRedraw = True
   
    For i = 1 To NoFlashes
    For ii = 1 To 2
        DoEvents
        PicBox.ForeColor = PresentColor
       
               
         
        For xx = 0 To PicBox.ScaleWidth / 15
            For yy = 0 To PicBox.ScaleHeight / 15
                If Black(xx, yy) = True Then
               
                    PicBox.Line (xx * 15, yy * 15)-((xx + 1) * 15, (yy + 1) * 15)
                End If
            Next yy
            yy = 0
        Next xx
       
        If PresentColor = ColorToFlashTo Then
            PresentColor = ColorToFlashFrom
        ElseIf PresentColor = ColorToFlashFrom Then
            PresentColor = ColorToFlashTo
        End If
       
       
    Next ii
    Next i


End Sub


FlashColors Picture1, "C:\pa5.bmp", &HFFFFFF, &H0&, 3






So basically what this will do is flash all the white pixels in a picture to black...which is I believe a start at this...White is not considered a skin color however I can scan an image for multiple skin tone hex values and if it detects a high majority of these pixels than you can assume than the image doesnt contain clothing...i am having trouble with this however I am not sure what would be a considerable integer to compare it..like say I have a list of all pigment hex value's from a light pinkish to a dark pinkish to a light brown to a darker brown the combination of hex values could be in the thousands...so is there some basic sequence to hex values in a sence that if a light brownish pigment color hex value is = 84A6D6 ..is there a sequence to getting the next darger hex value ? possibly like 84A6D7 84A6D8 84A6D9...etc..if there is a hex sequence that it follows I could check for all possible sequences of a pigment hex value..and loop it..the reason i would poing to the middle of the image is because..if i could bitblt the pictures background in some manner then get the mid section of the body if it doesnt return and red,green,blue value then you can condider that there isn't any clothing there..something to that extent..

Try that above code and if your willing to maybe give me some insight or a possible way that might be able to check for clothing on an image..i think the above code is a start..but I need expert opinion.. thanks..
0
 
SizeyCommented:
A Hex colour is basically a nice representation of a Long colour, as each pair of digits is the equivalent to RGB, eg:

&HEF34BC is the same as:
 Blue = EF (hex) =  239 of 255 (decimal)
 Green = 34 (hex) =  52 of 255 (decimal)
 Red = BC (hex) =  188 of 255 (decimal)

The first piece of code in my previous post does this conversion (from a Hex or Long value).

For comparisons, you can start from a base number (say &H223344), and loop through all possible combinations up to your target value (say &H334455).
You would need to loop through all values for all 3 sections (R,G,B) of the number, which would take a long time (in this example nearly 5000 checks per pixel). My previous post did essentially the same thing, but with only 1 check per pixel ;-)  

You just need to change the second half of my previous post so that appropriate values are checked, eg: for the check of &H223344 to &H334455 you could use this inside your "For y" loop:

        Black(x, y) = False
Dim lPointColour as Long  '(using an extra variable to reduce calls to the .Point method)
Dim lRed as Long, lGreen as Long, lBlue as Long
        lPointColour = PicBox.Point(x * 15, y * 15)
        lRed = lPointColour Mod 256
        lGreen = (lPointColour \ 256) Mod 256
        lBlue = (lPointColour \ 256) \ 256

        If (lRed >= &H22) and (lRed <= &H33) Then
          If (lGreen >= &H33) and (lGreen <= &H44) Then
            If (lBlue >= &H44) and (lBlue <= &H55) Then
              Black(x, y) = True
            End If
          End If
        End If

This does of course mean that you will change the colour when the flash comes back to the original colour, so rather than use your Black array, I would recommend using an array of a Type (which contains the pixels X, Y, and Colour), and loop through this array for your flashing.

The hard task is to find the appropriate range of values.
0

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 9
  • 7
Tackle projects and never again get stuck behind a technical roadblock.
Join Now