trace the outline of a shape on a bitmap **RESOLVED**
I have a picturebox with a bitmap. White background with red circle in it.
How do I trace the edge of the red circle so I can reproduce its position and size with code onto another picturebox.
My thought was to start at 0,0 and check each pixel row by row. For each row remember the first and last red pixel and change it to black to indicate the edge to the user.
The shape on the image is owner drawn and will always be the same colour though the size and shape may differ.
Last edited by davidrobin; Mar 21st, 2003 at 09:51 AM.
You can always try checking the neighbor of each pixel
If any rook-wise adjacent neighbor is red and the pixel it self is not red then set the pixel to black.
There are better (faster) ways but this should work.
Pseudocode:
Code:
For each pixel (x, y)
If ( (x, y) != RED ) Then
If ( (x, y-1) = RED ) or ( (x, y+1) = RED ) or
( (x-1, y) = RED ) or ( (x+1, y) = RED ) Then
SetPixel (x,y) = BLACK
End If
End If
End For
To further complicate things I have to recreate the shape in another picture box (for example) by using the polygon function. There fore I literally have to trace round the perimeter of the shape starting and ending at the same point.
Find one pixel that lies on the edge of the shape you want to trace around. This is your current pixel.
Look at the eight adjacent neighbors for a place where the color changes (in your case from red to white)
When looking for that point where the edge changes always start your next search where you left off in the last search.
That way things go fast for large straing edges
If that point is NOT the desired color then move counter-clockwise around the edge until it is.
If the point is the desired color move clockwise around the edge until it changes.
Select that edge pixel as your new current pixel. Repeat above until you get back to, or near, the first pixel.
Beacuse some shapes might be very large recording every pixel to map back onto a polygon can get quite tedious. To fix this, just save every 3rd or 5th pixel on the edge until you get back to the starting pixel.
You can also eliminate points by checking whether the 3rd pixel in a series of pixels lies on the same line as the previous two pixels. If so then delete the second pixel.
Illustrator,
I kind of understand what you are suggesting but am not being very successful at implementing it.
I have included a framework with a picture box and image. The image is a circle (nice and simple) At the moment the program looks along each row at every pixel until it finds a red pixel. At that point my abilities fail me.
I have included an image with a less standard shape on it as this program will have no idea what the image shape may be.
If anyone can fill in a bit of code I would be grateful
I wrote some code that works quite well for what you are doing.
There are a couple of cases where the program will have troubles
1) if the red image ever touches the edge of the picture box
2) if there are thin white lines breaking up the image
3) if the image has inside components (like a donut)
The above cases can be fixed with a little creativity.
Anyways here is what I wrote (you should be able to make it more efficient, especially the clockwise and counterclockwise looping rountines)
Code:
Dim row As Integer, startrow As Integer
Dim column As Integer, startcolumn As Integer
Dim i As Integer
Dim j As Integer
Dim EdgePoints() As POINTAPI
Dim PointCount As Integer
'start in the image corner
row = 0
column = 0
'while we are not on an edge and not at the end of the image
While ((Picture1.Point(column, row) <> vbRed) And (row <> Picture1.ScaleHeight))
column = column + 1
If (column = Picture1.ScaleWidth) Then
column = 0
row = row + 1
End If
Wend
'if not at the end of the image start the trace routine
If (row <> Picture1.ScaleHeight) Then
'set the the point above as the start point
startrow = row - 1
startcolumn = column
'set the next starting point
column = column - 1
'save the first points
PointCount = 2
ReDim Preserve EdgePoints(PointCount)
EdgePoints(1).X = startcolumn
EdgePoints(1).Y = startrow
EdgePoints(PointCount - 1).X = column
EdgePoints(PointCount - 1).Y = row
'draw the two border points
Picture1.PSet (startcolumn, startrow), QBColor(0)
Picture1.PSet (column, row), QBColor(0)
i = 0
j = 1
While ((column <> startcolumn Or row <> startrow) And PointCount < 10000)
'scan clockwise if red
While (Picture1.Point(column + i, row + j) = vbRed)
If (i = -1 And j = -1) Then
i = i + 1
ElseIf (i = 0 And j = -1) Then
i = i + 1
ElseIf (i = 1 And j = -1) Then
j = j + 1
ElseIf (i = 1 And j = 0) Then
j = j + 1
ElseIf (i = 1 And j = 1) Then
i = i - 1
ElseIf (i = 0 And j = 1) Then
i = i - 1
ElseIf (i = -1 And j = 1) Then
j = j - 1
ElseIf (i = -1 And j = 0) Then
j = j - 1
End If
Wend
'scan counter-clockwise is not red
While (Picture1.Point(column + i, row + j) <> vbRed)
If (i = -1 And j = -1) Then
j = j + 1
ElseIf (i = 0 And j = -1) Then
i = i - 1
ElseIf (i = 1 And j = -1) Then
i = i - 1
ElseIf (i = 1 And j = 0) Then
j = j - 1
ElseIf (i = 1 And j = 1) Then
j = j - 1
ElseIf (i = 0 And j = 1) Then
i = i + 1
ElseIf (i = -1 And j = 1) Then
i = i + 1
ElseIf (i = -1 And j = 0) Then
j = j + 1
End If
Wend
'step back to the last white pixel
If (i = -1 And j = -1) Then
i = i + 1
ElseIf (i = 0 And j = -1) Then
i = i + 1
ElseIf (i = 1 And j = -1) Then
j = j + 1
ElseIf (i = 1 And j = 0) Then
j = j + 1
ElseIf (i = 1 And j = 1) Then
i = i - 1
ElseIf (i = 0 And j = 1) Then
i = i - 1
ElseIf (i = -1 And j = 1) Then
j = j - 1
ElseIf (i = -1 And j = 0) Then
j = j - 1
End If
'update pixel position
column = column + i
row = row + j
'draw the border
Picture1.PSet (column, row), QBColor(0)
'add the new point to the data set
PointCount = PointCount + 1
ReDim Preserve EdgePoints(PointCount)
EdgePoints(PointCount - 1).X = column
EdgePoints(PointCount - 1).Y = row
Wend
End If
MsgBox "PointCount:" & PointCount
I have given it quick run through and it works fine. Absolutely amazing, well it is for me all I do at work is database programming (SQL 7) and vb. Nothing exciting like this.
if theres a red circle with a hole in, Illuminator's code wont fill those outlines....
this code fills all outlines! just click the BG....it also has a function to store object info...that means that it stores all objects pixel colors in a stack to be redrawn with another background (first click the BG, then on the other pic)...
check it out!
Cyborg: Certainly puts a different perspective on how to do it.
Originally posted by Illuminator I wrote some code that works quite well for what you are doing.
There are a couple of cases where the program will have troubles
1) if the red image ever touches the edge of the picture box
2) if there are thin white lines breaking up the image
3) if the image has inside components (like a donut)
The above cases can be fixed with a little creativity.
It is a requirement that the above scenarios do not happen. I will put some checks in my code to make sure.
dunno....have never used that api.....i just store those pixels positions and colors in an array and then go through that array and use setpixelv with the .x, .y and .color