FAST color counting of a pixmap.
BlitzMax Forums/BlitzMax Beginners Area/FAST color counting of a pixmap.
| ||
Hello, I wrote a function to count and store the number of colors in a pixmap derived from a timage. My problem is that the function is very slow. My function does work great but again speed is a HUGE issue. I would advise testing it with an extreamly low color image. It took about 5 minutes to process a normal photograph. I have created my own color object that stores values and have created a list to keep track of the color objects. This function will be built upon later and I will eventually need a tlist full of unique color objects. Type color_data Field r#=0,g#=0,b#=0,h#=0,s#=0,v#=0 ' R=0-255 'G=0-255 'B=0-255 'H=0-360 degrees 'S=0-1 'V=0-1 Method set_rgb(R#,G#,B#) 'set the r g b values directly then adjust the hsv Self.r=R Self.g=G Self.b=B ' *now update hsv values* ' now we need to convert our HSV values to base 1 R=R/255 G=G/255 B=B/255 ' find the largest and smallest of the rgb colors Local Max_#=Max(r,Max(g,b)) Local Min_#=Min(r,Min(g,b)) ' solve the hue value ' if max=min (no hue) hue is irrevelant here so we will just go with 0 If max_=min_ Self.h=0 End If ' if red is the highest with green greater or equal to blue If max_= R and G >= B Self.h=60*( (G-B)/(max_-Min_))+0 End If ' if red is the highest with green less than blue If max_= R and G < B Self.h=60*( (G-B)/(max_-Min_))+360 End If ' if the max is green If max_=G Self.h=60*( (B-R)/(max_-Min_))+120 End If ' if the max is blue If max_=B Self.h=60*( (R-G)/(max_-Min_))+240 End If ' now for the saturation value If max_ = 0 Self.s=0 Else Self.s= 1-(min_/max_) End If ' finally the value Self.v=max_ End Method Method set_hsv(H#,S#,V#) 'set the h s v values directly then adjust the r g b values Self.h=H Self.s=S Self.v=V ' now start adjusting the RGB values Local Hi%=(H/60.0) mod 6.0 Local f#=(H/60.0)-Hi Local p#=V*(1.0-S) Local q#=V*(1.0-(f*S)) Local t#=V*(1.0-(1.0-f)*S) ' now preform the checks If Hi = 0 R=V G=t B=p End If If Hi = 1 R=q G=V B=p End If If Hi = 2 R=p G=V B=t End If If Hi = 3 R=p G=q B=V End If If Hi = 4 R=t G=p B=V End If If Hi = 5 R=V G=p B=q End If 'convert to base 255 Self.r=R*255.0 Self.g=G*255.0 Self.b=B*255.0 EndMethod End Type Function count_colors(source:TImage) Local sourcePixmap:TPixmap = LockImage(source) Local sourcePixmapPtr:Byte Ptr = PixmapPixelPtr(sourcePixmap, 0, 0) Local tmpPixmap:TPixmap = CopyPixmap(sourcePixmap) Local tmpPixmapPtr:Byte Ptr = PixmapPixelPtr(tmpPixmap,0,0) Local x:Int,y:Int Local w:Float = tmpPixmap.width Local h:Float = tmpPixmap.height Local color_list:TList = New TList For Local x:Float = 0.0 To w - 1 For Local y:Float = 0 To h - 1 Local red = sourcePixmapPtr[x * 4 + y * 4 * w] Local green = sourcePixmapPtr[x * 4 + y * 4 * w + 1] Local blue = sourcePixmapPtr[x * 4 + y * 4 * w + 2] Local my_color:color_data = New color_data my_color.set_rgb(red, green, blue) ' see if the color is in the lsit Local in_list:Int = 0 For Local i:color_data = EachIn(color_list) If my_color.r = i.r If my_color.g = i.g If my_color.b = i.b in_list = 1 Exit End If EndIf End If Next If Not in_list ListAddLast(color_list, my_color) End If Next Next Notify("There are " + CountList(color_list) + " colors in this image.") Return Null EndFunction |
| ||
I think there are algorithms for this kind of thing, like building a histogram, which should be much faster. Counting colors should be pretty quick to do. |
| ||
I think so too. The problem is storing the data for later use. I need to build a virtual palette of colors used and I think this adds some extensive overhead. I'm fairly new to pixmap editing and such. |
| ||
I made this in afew minutes.Type color_counter Field colors:Tmap Method New() colors=CreateMap() EndMethod Method Add(r,g,b) Local i If MapContains(colors,tos(r,g,b)) i=Int(String(MapValueForKey(colors,tos(r,g,b)))) EndIf i:+1 MapInsert(colors,tos(r,g,b),String(i)) EndMethod Method Count:Int() Local i For Local o:Object=EachIn MapKeys(colors) i:+1 Next Return i EndMethod Function Tos:String(r,g,b) ' rgb to a string Return String(r)+"-"+String(g)+"-"+String(b) EndFunction EndType Function count_colors(source:TImage) Local sourcePixmap:TPixmap = LockImage(source) Local sourcePixmapPtr:Byte Ptr = PixmapPixelPtr(sourcePixmap, 0, 0) Local tmpPixmap:TPixmap = CopyPixmap(sourcePixmap) Local tmpPixmapPtr:Byte Ptr = PixmapPixelPtr(tmpPixmap,0,0) 'Local x:Int,y:Int Local w:Int = tmpPixmap.width Local h:Int = tmpPixmap.height Local colors:color_counter=New color_counter For Local x:Int = 0 To w - 1 For Local y:Int = 0 To h - 1 Local red = sourcePixmapPtr[x * 4 + y * 4 * w] Local green = sourcePixmapPtr[x * 4 + y * 4 * w + 1] Local blue = sourcePixmapPtr[x * 4 + y * 4 * w + 2] colors.Add(red,green,blue) Next Next Notify("There are " + colors.Count() + " colors in this image.") Return Null EndFunction |
| ||
How about a) Create Type 'Pixel_colour' with fields argb, (possibly a,r,g,b) and count b) Create a TMap. c) Readpixel d) Check whether there is a TMAP entry for string(argb) e) If not, create a map entry with a new pixel_colour indexed to string(argb) f) Repeat from c). <edit> Took out code as didn'treally see Azathoth's post. |
| ||
Yah, some kind of hash table thing, or some other kind of acceleration structure. |
| ||
Thanks for the help. Where are "maps" discussed in the help menu? |
| ||
Other than a command list... they're not. This might help |
| ||
Ok I thought I may have missed something. Damn shame so much basic information is missing in the documentation. Well, that comment is a bit like beating a dead horse now. |
| ||
I tried a different way of counting colors in images, working from what i've seen here. Fastest way is probobly array: Code is an example, should be able to run this as is. Colors: 178127 Width: 2304 Height: 1728 {3981312 pixels Processed in 918ms} |
| ||
Seems pretty speedy: Colors: 434230 Width: 2288 Height: 1712 {3917056 pixels Processed in 175ms} or a huge image: Colors: 89588 Width: 5400 Height: 3600 {19440000 pixels Processed in 662ms} (Core 2 Duo E6600 @ 2.4GHz) |
| ||
Yeah, calls to lists or maps is an organized way of checking previously found colors, but extremely non-optimistic in terms of speed. With the array, you only check once if a color has been met. With tlist/maps your checking EVERY single pixel whether a color has been added or not. With an array, you can even count how many times a color was found, with no real loss in speed (talking 5-10%, maybe). My method (derived from burnsides pixel ptr method of getting r,g,b values) could probobly still be optimized some. I would assume this is the idea that programs like paintshop pro & adobe use for counting colors. |