B4J Question [Solved] Wand tool

jroriz

Active Member
Licensed User
Longtime User
img1.PNG
to
img2.PNG

Is it possible to create something like a wand tool?

I need to change all the yellow pixels, and then pixels "close to yellow", to black.
Then all the rest to white.

I was trying using BitmapCreator, and comparing pixel by pixel. But i could not figure out how to detect a range of pixels that are close to yellow.

I think this is a hard one...
 

Roycefer

Well-Known Member
Licensed User
Longtime User
There are a few ways to think about the "closeness" of a color to another color. A simple method that might work for you is to consider the RGB color space as a 3-dimensional euclidean space and just calculate distances using Pythagorean Theorem:
B4X:
Dim colorDist As Double = Sqrt(Power(r1-r2,2)+Power(g1-g2,2)+Power(b1-b2,2))
Then you can select a maximum permissible distance.

There are more sophisticated ways that involve conversion to HSB color space or treating RGB color space as a vector space and taking inner products. If the Pythagorean Theorem method doesn't suit your needs, we can explore the more sophisticated solutions.

Note that in the example picture you posted, the non-yellow pixels are all black. If this is representative of the real images you have to use, then it will be easier to just detect black pixels and turn them white and non-black pixels to black.
 
Upvote 0

MarkusR

Well-Known Member
Licensed User
Longtime User
But i could not figure out how to detect a range of pixels that are close to yellow.
i answered in your other post.
yellow is just red+green
 
Upvote 0

Sandman

Expert
Licensed User
Longtime User
It might help more if you described how you plan to use the resulting image. I come from a Photoshop background (GIMP nowadays) so I tend to think more in that way than a strictly mathematical way that @Roycefer described.

So, if you plan to use the resulting image as something you just display or perhaps place on top of another image, I would do this to get a perfect result.

  1. Starting image
    65436-d333655f54ee2013bb095bcad881d367.jpg


  2. Throw away all colour, making it grayscale
    upload_2018-9-26_9-50-14.png


  3. Invert it
    upload_2018-9-26_9-50-58.png


  4. Adjusting the Levels by setting the background to white (this is not same as working with brightness, the latter destroys image data, Levels ”stretches” the available data)
    upload_2018-9-26_9-52-24.png

    (In this specific case I didn't have to set the blackpoint, the yellow was good enough to work with)

  5. And, if that's what you’re aiming for, multiplying the image into another picture:
    upload_2018-9-26_9-55-40.png
 
Upvote 0

MarkusR

Well-Known Member
Licensed User
Longtime User
it would look better with a lossless format like png.
grey = (r+g+b) / 3

B4X:
Sub Process_Globals
 
   Private xui As XUI
   Private ImageView1 As ImageView
   Private ImageView2 As ImageView
  ...

Sub Test
 
    Dim Bitmap1 As B4XBitmap = xui.LoadBitmap(File.DirAssets,"clock.png")
 
    ImageView1.SetImage(Bitmap1) '< a ImageView in layout
 
    Dim bc As BitmapCreator
    bc.Initialize(Bitmap1.Width,Bitmap1.Height)
    bc.CopyPixelsFromBitmap(Bitmap1)
 
    Dim x As Int
    Dim y As Int
    Dim pixel As ARGBColor
    For x = 0 To bc.mWidth-1
        For y = 0 To bc.mHeight-1
            bc.GetARGB(x,y,pixel)
            If pixel.r > 64 And pixel.b < 64 Then
                pixel.r = pixel.r / 2
                pixel.g = pixel.g / 4
                pixel.b = pixel.b / 4
            Else
                pixel.r =255
                pixel.g =255
                pixel.b =255
            End If
            bc.SetARGB(x,y,pixel)
        Next
    Next
 
    ImageView2.SetImage(bc.Bitmap) '< a ImageView in layout
 
    Bitmap1 = bc.Bitmap
    Dim Out As OutputStream
    Log(File.DirTemp)
    Out = File.OpenOutput(File.DirTemp, "Output.png", False)
    Bitmap1.WriteToStream(Out, 100, "PNG")
    Out.Close
 
End Sub

Snap_2018.09.26_14h26m25s_002.png
 
Last edited:
Upvote 0

jroriz

Active Member
Licensed User
Longtime User
There are a few ways to think about the "closeness" of a color to another color. A simple method that might work for you is to consider the RGB color space as a 3-dimensional euclidean space and just calculate distances using Pythagorean Theorem:
B4X:
Dim colorDist As Double = Sqrt(Power(r1-r2,2)+Power(g1-g2,2)+Power(b1-b2,2))
Then you can select a maximum permissible distance.

There are more sophisticated ways that involve conversion to HSB color space or treating RGB color space as a vector space and taking inner products. If the Pythagorean Theorem method doesn't suit your needs, we can explore the more sophisticated solutions.

Note that in the example picture you posted, the non-yellow pixels are all black. If this is representative of the real images you have to use, then it will be easier to just detect black pixels and turn them white and non-black pixels to black.

It was perfect, using the first suggestion (from Roycefer). I did not test the others, but I greatly appreciate it!
The result was better than I expected.
The code I got is below. If anyone can improve it, I'm grateful.
Thank you all. This forum is simply fantastic!

Final code:

B4X:
Sub Clean(Whichcolor As Int, bc As BitmapCreator)
   
    Dim x, y As Int
    Dim ProtectedColor, ARGB As ARGBColor
   
    bc.ColorToARGB(Whichcolor, ProtectedColor)
   
    For x = 0 To bc.mWidth-1
        For y = 0 To bc.mHeight-1

            bc.GetARGB(x, y, ARGB)
            Dim colorDist As Double = Sqrt(Power(ProtectedColor.r-ARGB.r,2)+Power(ProtectedColor.g-ARGB.g,2)+Power(ProtectedColor.b-ARGB.b,2))
           
            If colorDist > 200 Then
                bc.SetColor(x,y,xui.Color_White)
            Else
                bc.SetColor(x,y,xui.Color_black)
            End If
        Next
    Next

End Sub

How to call: Clean(xui.Color_white, bc), where bc is bitmapcreator.
 
Last edited:
Upvote 0
Top