Remove white in a photo (image)

Take an ID photo taken with a white background.

I want to programmatically remove the white background (keep non surrounding white from the photo).

After a long and boring search (in French, then in English) I found nothing (everything I found was using Photoshop, Gimp, some web sites, etc.).

Back 25 years ago, I had an HyperCard stack who provide this function, but that was 25 years ago and I never saw (watched) the code, then.

All clues, ideas, links to code, etc. is welcome.

The idea is to make a search, line after line (left to right / top to bottom of the image), for the first non white pixel: easy to search for the first pixel (from left to right), but I do not know how to do the same for the last non white pixel.

Think at a circle: once you found the first non white pixel, how can I know where is the last non white pixel o the other side of the circle ?

As far as I recall, the found pixel location are stored in an array of x,y and then later used as parameter in a DrawPolygon.

You could very crudely loop through each pixel and any pixel that is close to white, you make transparent.

Assuming it is a desktop app.

Create a non alpha image, draw the picture into it, and set transparent = 1.

For iOS I tried the pixel per pixel method, it is rather slow.

Thank you guys.

I certainly forget to saysomething, because I do not ant to set the white transparent, but “remove them“ (crop the image based on the surrounding white ?).

Sam:
I can follow your advice for the left part of the line (more or less easy), but what about the right part of the line ?$(how can I “know” where the first white pixel is the end of the "image part I want to keep” ?
After posting the question, I played a little bit with this idea, but it was too late in the night for my brain to work above 10% :wink:
Oldster trouble(s).

Michel:
I already do that for some images, I save them and crop the images using Preview. But here, I want to crop them in the application.

Also: I certainly forgot to say something (after re-reading my post) or mixing two ideas together. By this afternoon or tomorrow, things will probebly be clearer in my mind.

You keep going until the end of the row and then step down a row and go again.

As Michel says, it’s a slow and crude method can be done in Core Image faster, but the amount of time to create that in Core Image is a lot and of course it would be Mac only.

I am always amazed at the way you complexify stuff as you go.

Transparent :
http://documentation.xojo.com/index.php/Picture.Transparent

Cropping (which was not part of your OP):
http://documentation.xojo.com/index.php/Graphics.DrawPicture

Graphics.DrawPicture ( Image as Picture, X as Integer, Y as Integer [,DestWidth as Integer ] [, DestHeight as Integer ] [, SourceX as Integer ] [, SourceY as Integer ] [, SourceWidth as Integer ] [, SourceHeight as Integer] )

Use the 4 last parameters to crop.

Now the UI you have to figure out for yourself. There are tons of examples around, such as Preview. Just emulate.

All this is cross-platform.

Yes, it was: I want to programmatically remove the white background.

DrawPicture: this set a square (or rectangle). I want to remove all white whatever the white area can be.

Nice ! Thanks.

Maybe this title better reflect what I want to do: “I want to create a magic wand”.

I am reading the code here .

I know Einhugar’s PictureEffects plugin has some great tools and color replace functions.

FWIW, I’ve done this in applications and it’s not as slow as you’d think if the images aren’t too big.

Assuming the “white” is a background in the photograph, you’ll need to set a threshold for what constitutes white because it’ll never be truly white, what with different types of lighting, shadows from the subject, etc.

Sam’s got the right idea, but I suggest that since you are cropping that you go from the edges to the center. 0 to width/2, width down to width/2, 0 to height/2 (assuming the person is always basically centered in the frame. On each pass, when you reach a non “white” pixel, you move the cropping edge outward. Something like:

[code]dim left, right, top as integer
// set worst case scenarios
Left = g.width
Right = 0
Top = g.height
for y = 0 to g.height
Dim LeftEdgeReached, RightEdgeReached as Boolean
for x = 0 to g.width/2
If not leftEdgeReached and isWhite(g.pixel(x,y)) = False then
Top = min(top,y)
Left = min(Left, x)
LeftEdgeReached = true
End if

If not rightEdgeReached and isWhite(g.pixel(g.width-x,y)) = False then
  Top = min(top,y)
  Right = max(right, g.width - x)
  RightEdgeReached = true
End if

// stop working the row if we got both values
If RightEdgeReached and LeftEdgeReached then
  Exit For I
End if

Next x
Next y [/code]

You’ll have to define the isWhite method and at the end, use the left, top and right to make a new picture and draw the old one into it using the offsets.

typed this from memory, but I think it’s right.

Edit: use #pragma BackgroundTasks Off at the beginning to squeeze a little more speed out of it.

Very interesting!

The code example looks good, but how do you delete the white pixel? Is there a if missing?

[code]if isWhite(g.pixel(g.width-x,y)) = true then

deletepixel

end if[/code]

What is the correct command to delete the pixel?

You don’t “delete” a pixel. you set its color, and/or its opacity… and usually the opacity is controlled via the Alpha layer or mask

Here is a brute force app I use to apply MASKS to all the icon graphics I use.

it scans a directory, takes all the PNG files, builds a transparent mask based on the WHITE in the main image.
For those parts I want to stay “WHITE”… I edit the picture ahead and replace &cFFFFFF with &cFEFEFE

    Dim dlg As OpenDialog
    Dim f As FolderItem
    Dim p As picture
    Dim g As graphics
    Dim p_rgb As RGBSurface
    Dim m_rgb As RGBSurface
    Dim x As Integer
    Dim y As Integer
    Dim i As Integer
    Dim f2 As FolderItem
    f2=GetFolderItem("")
    For i=1 To f2.Count
        f=f2.TrueItem(i)
        If Right(f.DisplayName,4)<>".png" Then Continue
        p=picture.Open(f)
        g=p.Graphics
        p_rgb=p.RGBSurface
        m_rgb=p.Mask.Rgbsurface
        m_rgb.FloodFill(0,0,&c000000)
        For x=0 To g.Width
            For y=0 To g.Height
                //if p_rgb.pixel(x,y)=&c000000 then p_rgb.pixel(x,y)=&cffffff
                If p_rgb.Pixel(x,y)=&cffffff Then 
                    m_rgb.pixel(x,y)=&cffffff
                Else
                    m_rgb.pixel(x,y)=&c000000
                End If
            Next y
        Next x
        p.Transparent=1
        p.Save(f,picture.SaveAsPNG)
    Next i
    MsgBox "done"
    Quit
    

[quote=346174:@Dave S] m_rgb=p.Mask.Rgbsurface [/quote]

I always get a Nil Object Exception at this line?

http://documentation.xojo.com/index.php/Picture.Mask
Not every picture has a Mask, the docs explain that pictures created with alpha channels do not.

some image types have no mask
they use an alpha channel instead
so the mask is nil

Posted above : http://documentation.xojo.com/index.php/Picture.Transparent