Home » Category » Microsoft Visual Basic

Microsoft Visual Basic: Colors in a picture box (vb6)

200| Wed, 07 May 2008 04:00:00 GMT| alejandro_carrascal| Comments (9)
Hello,

I have an image in a PictureBox. I would like to create a list with all the
colors shown. I was thinking reading each pixel in the picture box and
storing the list of colors I'm finding as I read. Is this the best way to
find the distinct colors that are present in the image?

Thank you!

Keywords & Tags: colors, picture, box, vb6, microsoft, visual basic

URL: http://www.programmerbase.com/visual-basic/80651/
 
«« Prev - Next »» 9 helpful answers below.
"Alejandro Carrascal" <alcasa...hotmail.com> wrote in message
news:43d2cb25_3...x-privat.org...
> Hello,
> I have an image in a PictureBox. I would like to create a list with all
> the
> colors shown. I was thinking reading each pixel in the picture box and
> storing the list of colors I'm finding as I read. Is this the best way to
> find the distinct colors that are present in the image?


I can't help you, but I just HAVE to know why on earth you would need to
LIST all the colors used in the picture? This could be in the thousands.
Sure, any decent paint or drawing program can capture a color, but listing
EVERY one of them?

I don't much much about picture formats, but if the picture is even just 800
x 600, checking 480,000 pixels, I'd think, would be quite time-consuming.
Now throw in the possibility of there even being just 500 hundred different
colors used. When you get to the 480,000th pixel, you'd be checking its
color against at least 499 other colors. I just can't see this being
feasible.
Mike
Microsoft MVP Visual Basic

miked | Wed, 07 May 2008 04:01:00 GMT |

Reading each Pixel colour is the easy and quick part, Even with a picture
the size of the screen I expect this could be done in less that a second.
Storing the list of colours in an array of longs would be quite quick to,
Still less that a second.
Finding the destinct colours?
This is where you go and hire a DVD and watch it while the processing is
done.
It's possible, but would take a very very long time unless the picture is
very small.

Ivar
"Alejandro Carrascal" <alcasa...hotmail.com> wrote in message
news:43d2cb25_3...x-privat.org...
> Hello,
> I have an image in a PictureBox. I would like to create a list with all
> the
> colors shown. I was thinking reading each pixel in the picture box and
> storing the list of colors I'm finding as I read. Is this the best way to
> find the distinct colors that are present in the image?
> Thank you!
>

ivar | Wed, 07 May 2008 04:02:00 GMT |

On Sat, 21 Jan 2006 19:54:11 -0500, "MikeD" <nobody...nowhere.edu>
wrote:
>I don't much much about picture formats, but if the picture is even just 80
0
>x 600, checking 480,000 pixels, I'd think, would be quite time-consuming.
>Now throw in the possibility of there even being just 500 hundred different
>colors used. When you get to the 480,000th pixel, you'd be checking its
>color against at least 499 other colors. I just can't see this being
>feasible.


I dunno. I use an old version of PSP (5.0) and it will count and
report # of colors used on a much larger 32-bit image in well under a
second. No doubt highly optimized code, but with 3ghz+ CPUs being the
norm these days...

-Tom
MVP - Visual Basic
(please post replies to the newsgroup)

tomesh | Wed, 07 May 2008 04:03:00 GMT |

I was able to get this to work like this (Probably not the best way, but it
works, and it is quite fast. My P4 2.8ghz can process an image with 500,000
+ pixels in about 15 seconds.): Just add a command button, 2 picture boxes,
and a progress bar to the form, and reference ADO If you put the individual
colors into an array, and sorted the array, you might even be able to get it
faster, ADO obviously has a good deal of overhead

Private Sub Command1_Click()
Dim i As Long
Dim X As Single
Dim Y As Single
Dim rsColors As Recordset
Dim currentPoint As Long
Dim tmpColor As Long
Dim colorArray() As Long

Set rsColors = New Recordset
rsColors.Fields.Append "Color", adBigInt

rsColors.Open
ReDim colorArray(Picture1.Picture.Width / Screen.TwipsPerPixelX *
Picture1.Picture.Height / Screen.TwipsPerPixelY)

ProgressBar1.Min = 0
ProgressBar1.Max = UBound(colorArray)

On Error Resume Next
For i = 1 To UBound(colorArray) + 1
X = (i Mod (Picture1.Picture.Width / Screen.TwipsPerPixelX)) - 1
Y = Int(i / (Picture1.Picture.Width / Screen.TwipsPerPixelX))

tmpColor = Picture1.Point(X, Y)
rsColors.AddNew
rsColors.Fields("Color") = tmpColor
If i Mod 100 = 0 Then
ProgressBar1.value = i
End If
'Debug.Print colorArray(i - 1)
Next
rsColors.Update

Picture2.ScaleMode = vbPixels
Dim lastColor As Long
lastColor = -1

rsColors.Sort = "Color"
Do Until rsColors.EOF
tmpColor = rsColors("Color")
If lastColor <> tmpColor Then
currentPoint = currentPoint + 1

X = currentPoint Mod Picture2.ScaleWidth
Y = Int(currentPoint / Picture2.ScaleWidth)

Picture2.PSet (X, Y), tmpColor
End If
rsColors.MoveNext
Loop

MsgBox "Image Contains " & rsColors.RecordCount & " Pixels, With " &
currentPoint & " Unique Colors."
End Sub

"Alejandro Carrascal" <alcasa...hotmail.com> wrote in message
news:43d2cb25_3...x-privat.org...
Hello,

I have an image in a PictureBox. I would like to create a list with all the
colors shown. I was thinking reading each pixel in the picture box and
storing the list of colors I'm finding as I read. Is this the best way to
find the distinct colors that are present in the image?

Thank you!

lancewynn | Wed, 07 May 2008 04:04:00 GMT |

> I have an image in a PictureBox. I would like to create a list with all
the
> colors shown. I was thinking reading each pixel in the picture box and
> storing the list of colors I'm finding as I read. Is this the best way to
> find the distinct colors that are present in the image?


Have a look at the Octree colourspace quantisation methods, it uses a tree
structure to count colours and frequency as is usually used to find optimal
palettes however if you don't prune the leaf nodes at each level you'll get
a colour count instead.
Hope this helps,

Mike

- Microsoft Visual Basic MVP -
E-Mail: EDais...mvps.org
WWW: Http://EDais.mvps.org/

miked_sutton | Wed, 07 May 2008 04:06:00 GMT |

"Ivar" <ivar.ekstromer000...ntlworld.com> wrote in message
news:QdBAf.7633$OI3.7297...newsfe2-win.ntli.net...

> Finding the destinct colours? This is where you go and hire
>a DVD and watch it while the processing is done.


Actually you can do this sort of thing very quickly. There are a number of
different mehthods. One of the simplest (although not the fastest I suspect)
is shown below. The code currently reports the number of unique colours used
in the image, but that data telling you exactly what they are is also
gathered at the same time. All you need to do to display them in a ListBox
is to run through the "filled array" just once and any element that contains
a 1 indicates that the "element number" colour has been used in the image.

Here's the code. Paste it into a VB Form containing one Command Button abd
one Picture Box.

Mike

Option Explicit
Const BI_RGB = 0
Const DIB_RGB_COLORS = 0
Private Type BITMAPINFOHEADER
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
Private Type RGBQUAD
rgbBlue As Byte
rgbGreen As Byte
rgbRed As Byte
rgbReserved As Byte
End Type
Private SourceArray() As Long
Private SourceWidth As Long
Private SourceHeight As Long
Private Type BITMAPINFO
bmiHeader As BITMAPINFOHEADER
End Type
Private bmapinfo As BITMAPINFO
Private Declare Function GetDIBits Lib "gdi32" _
(ByVal hdc As Long, ByVal hBitmap As Long, _
ByVal nStartScan As Long, ByVal nNumScans As Long, _
lpBits As Any, lpBI As BITMAPINFO, _
ByVal wUsage As Long) As Long
Private Declare Function SetDIBitsToDevice Lib "gdi32" _
(ByVal hdc As Long, ByVal x As Long, ByVal y As Long, _
ByVal dx As Long, ByVal dy As Long, ByVal SrcX As Long, _
ByVal SrcY As Long, ByVal Scan As Long, _
ByVal NumScans As Long, Bits As Any, _
BitsInfo As BITMAPINFO, ByVal wUsage As Long) As Long
Private Declare Sub ZeroMemory Lib "kernel32.dll" _
Alias "RtlZeroMemory" _
(ByRef Destination As Any, ByVal Length As Long)
Private clrs() As Long

Private Sub Form_Load()
Picture1.ScaleMode = vbPixels
Picture1.AutoRedraw = True
ReDim clrs(0 To &HFFFFFF)
Picture1.AutoSize = True
Picture1.Picture = LoadPicture("c:\tulips.bmp")
End Sub

Private Sub Command1_Click()
Dim lret As Long, t1 As Single, t2 As Single
Dim x As Long, y As Long, p As Long, used As Long
t1 = Timer
' Much of the time to perform the task is actually taken
' by the Redim (1 to &HFFFFFF) line. I've actually written
' a modified version of this code (using the individual bits
' in Longs to store the "colour used" flag instead of using
' a Long variable. (Can't remember why I didn't use Bytes
' now, but I'm sure it'll come back to me ;-)
' The "bits" method inceased the speed dramatically
' (because even though the "bit logic" tended to slow things
' down a little the speed advantage gained by using only one
' eighth of the memory for the array speeded it up enormously.
' Sadly, I lost that code when I got rid of my old computer
' and forgot to save it!). Maybe I'll write it againm one day!
ZeroMemory clrs(0), &HFFFFFF * 4 + 1 ' a bit faster than redim
SourceWidth = Picture1.ScaleWidth
SourceHeight = Picture1.ScaleHeight
With bmapinfo.bmiHeader
.biSize = 40
.biWidth = SourceWidth
.biHeight = SourceHeight
.biPlanes = 1
.biBitCount = 32
.biCompression = BI_RGB
End With
ReDim SourceArray(1 To SourceWidth, 1 To SourceHeight)
lret = GetDIBits(Picture1.hdc, Picture1.Image, _
0, SourceHeight, SourceArray(1, 1), bmapinfo, _
DIB_RGB_COLORS)
' SourceArray now contains the pixel data, which you can
' transfer with Winsock (or do whatever else you want to
' do with it) before finally dumping it into another
' Picture Box of the same size.
' count the colours
For y = 1 To SourceHeight
For x = 1 To SourceWidth
p = SourceArray(x, y)
If clrs(p) = 0 Then
clrs(p) = 1
used = used + 1
End If
Next x
Next y
t2 = Timer
Caption = Format(t2 - t1, "0.0") & " seconds. Colours used = " _
& Format(used)
End Sub

mike_williams | Wed, 07 May 2008 04:07:00 GMT |

"Mike Williams" <Mike...WhiskyAndCoke.com> wrote in message
news:OZMLq50HGHA.648...TK2MSFTNGP14.phx.gbl...

. . . by the way, I forgot to mention the fact that a bitmap that you load
into a VB picture box or into a StdPicture object or anyhting of a similar
nature will be an exact copy of the original on disk *only* if your computer
is running at full colour depth. So if you want your code to work properly
on all machines (including those running at 16 bit colour depth or less)
then you need to instead get the original bitmap into a DIB structure
(device independent bitmap). You can use pretty much the same code on a DIB
with very slight modification.

Mike

mike_williams | Wed, 07 May 2008 04:07:00 GMT |

"Mike Williams" <Mike...WhiskyAndCoke.com> wrote in message
news:OZMLq50HGHA.648...TK2MSFTNGP14.phx.gbl...

. . . here's a slight modification of my earlier code, which is a bit faste
r
and which removes one tiny error. For best results compile to native code
and set advanced optimizations to remove array bounds checks and integer
overflow checks. On my own machine it counts the unique colours in a 1600 x
1200 pixel bitmap in a bit less than half a second. I've still got to modify
it to use individual bits as flags, but that can wait for another day :-)

Mike

Option Explicit
Const BI_RGB = 0
Const DIB_RGB_COLORS = 0
Private Type BITMAPINFOHEADER
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
Private Type RGBQUAD
rgbBlue As Byte
rgbGreen As Byte
rgbRed As Byte
rgbReserved As Byte
End Type
Private SourceArray() As Long
Private SourceWidth As Long
Private SourceHeight As Long
Private Type BITMAPINFO
bmiHeader As BITMAPINFOHEADER
End Type
Private bmapinfo As BITMAPINFO
Private Declare Function GetDIBits Lib "gdi32" _
(ByVal hdc As Long, ByVal hBitmap As Long, _
ByVal nStartScan As Long, ByVal nNumScans As Long, _
lpBits As Any, lpBI As BITMAPINFO, _
ByVal wUsage As Long) As Long
Private Declare Function SetDIBitsToDevice Lib "gdi32" _
(ByVal hdc As Long, ByVal x As Long, ByVal y As Long, _
ByVal dx As Long, ByVal dy As Long, ByVal SrcX As Long, _
ByVal SrcY As Long, ByVal Scan As Long, _
ByVal NumScans As Long, Bits As Any, _
BitsInfo As BITMAPINFO, ByVal wUsage As Long) As Long
Private clrs(0 To &HFFFFFF) As Byte

Private Sub Form_Load()
Picture1.ScaleMode = vbPixels
Picture1.AutoRedraw = True
Picture1.AutoSize = True
Picture1.Picture = LoadPicture("c:\dummypic.bmp")
End Sub

Private Sub Command1_Click()
Dim lret As Long, t1 As Single, t2 As Single
Dim x As Long, y As Long, p As Long, used As Long
Dim s1 As String
t1 = Timer
Erase clrs()
SourceWidth = Picture1.ScaleWidth
SourceHeight = Picture1.ScaleHeight
With bmapinfo.bmiHeader
.biSize = 40
.biWidth = SourceWidth
.biHeight = SourceHeight
.biPlanes = 1
.biBitCount = 32
.biCompression = BI_RGB
End With
ReDim SourceArray(1 To SourceWidth, 1 To SourceHeight)
lret = GetDIBits(Picture1.hdc, Picture1.Image, _
0, SourceHeight, SourceArray(1, 1), bmapinfo, _
DIB_RGB_COLORS)
' count the colours
For y = 1 To SourceHeight
For x = 1 To SourceWidth
p = SourceArray(x, y)
If clrs(p) = 0 Then
clrs(p) = 1
used = used + 1
End If
Next x
Next y
t2 = Timer
s1 = Format(t2 - t1, "0.0") & " seconds. Colours used = " _
& Format(used)
MsgBox s1
End Sub

mike_williams | Wed, 07 May 2008 04:09:00 GMT |

the following style using Picture1.Picture instead of Picture1.Image and
doesn't require setting the autoredraw, autosize or scalemode properties.

thanks for your information...

----
SourceWidth = ConvertPixelHimetric(Picture1.Picture.Width, LOGPIXELSX)
SourceHeight = ConvertPixelHimetric(Picture1.Picture.Height, LOGPIXELSY)

lret = GetDIBits(Picture1.hdc, Picture1.Picture, _
0, SourceHeight, SourceArray(1, 1), bmapinfo, _
DIB_RGB_COLORS)
---

'source: Mike D Sutton
(http://edais.mvps.org/Code/Librarie...OLEPicture.html)

Private Enum LOGPIXELS
LOGPIXELSX = 88
LOGPIXELSY = 90
End Enum
Private Declare Function CreateIC Lib "GDI32.dll" Alias "CreateICA" (ByVal
lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As
String, ByRef lpInitData As Any) As Long
Private Declare Function GetDeviceCaps Lib "GDI32.dll" (ByVal hdc As Long,
ByVal nIndex As Long) As Long
Private Declare Function MulDiv Lib "Kernel32.dll" (ByVal nNumber As Long,
ByVal nNumerator As Long, ByVal nDenominator As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long

Private Function ConvertPixelHimetric(ByVal inValue As Long, GDCFlag As
LOGPIXELS) As Long
Dim TempIC As Long

Const HimetricInch As Long = 2540

TempIC = CreateIC("DISPLAY", vbNullString, vbNullString, ByVal 0&)

If (TempIC) Then
ConvertPixelHimetric = MulDiv(inValue, GetDeviceCaps(TempIC,
GDCFlag), HimetricInch)
DeleteDC TempIC
End If
End Function

fatihargun | Wed, 07 May 2008 04:10:00 GMT |

Microsoft Visual Basic Hot Answers

Microsoft Visual Basic New questions

Microsoft Visual Basic Related Categories