Making G-code from images

This adapts my bit-map pixel routines so you can choose any type of bitmap image & from it create a series of instructions to an x-raster on a drum driven as y so a pen or extruder head can deposit ( or not) at each location. By calling an installed copy of ImageMagick it can load virtually any type of image file. LB can only display directly a BMP file, and by doing the conversiion I get the easy ability to read pixels directly from the BMP file, which is a loss-less raster file of the image. Think of a target machine like an old dot-matrix printer with only one pin available. Twenty years ago I used this technique to draw high-res paper graphs of temperature & sun strength- A3 width and twenty feet per month!

For this project ( from a query on the forums) it is to be envisaged as 'writing' onto a roll of paper or fabric. In LB you move to an x,y coordinate and set a pixel. On the intended machine you move to the point and emit a 'blob'!

The intention would be to create valid G-code for a machine that accepted it- but most of my machines I drive directly to the steppers & servos. The version below finishes with a massive file in the clipboard, ready to paste into an editor and save. Equally easy to automate that step. In my case I'd be sending commands directly via a USB 'serial' EiBotBoard driver to steppers & servos.

The image is scaled for you to width of 720 pixels, based on fabric width of 48 inches at 15 dpi, and roll length of the same- but easily changed. If the image is not square, it will have an empty border on one side. The image is displayed on-screen, and essentially raster scanned, although this is done for greater speed by reading the pixels direct from the saved BMP file. If a pixel brightness exceeds a threshold it sends a black or white pixel to over-write the original image, and to generate the instruction file. Strictly you should use a weighted mean of R, G and B, but I would normally pre-process the image anyway in GIMP or similar.

You need Liberty BASIC and ImageMagick to have been installed first.


'   **************************************************

'   ****  skeleton3b August 2013 tenochtitlanuk   ****

'   **************************************************

'   **  Requires installation of ImageMagick from   **
'   **  http://www.imagemagick.org/script/index.php **

'   Thanks to Rod and Anatoly ( tsh73) especially for
'       get-pixel routines and examples.

nomainwin

graphicbox #w.gb,   3,   2, 720, 720
texteditor #w.te, 730,   2, 200, 720

WindowWidth  =950
WindowHeight =780

open "Show image" for window as #w

#w    "trapclose quit"
#w.gb "down ; fill 100 100 100 ; flush"
#w.te "G90"
#w.te "; absolute coordinate positioning"

[openpic]
    filedialog "Open image for processing", "*.bmp; *.gif; *.jpg", fname$
    if fname$ ="" then goto [openpic]
    f$ =mid$( fname$, 2+len( DefaultDir$))
    f$ =word$( f$, 1, ".")
    file$  ="convert -sample 720x720 "; chr$(34); fname$; chr$( 34); " "; f$; "720x720.bmp"

    run "cmd.exe /k "; chr$( 34); file$; chr$( 34), HIDE

    timer 5000, [carryon]  '   allow 5 secs for image to be converted
    wait
[carryon]
    timer 0

    loadbmp "image", f$ +"720x720.bmp"
        #w.gb "drawbmp image 0 0"
        #w.gb "flush"
    unloadbmp "image"

dim r( 1, 1), g( 1, 1), b( 1, 1)
global bmpOffset, bmpColDepth, rasterWidth, BisOpen, GisOpen

BisOpen =0: GisOpen =0

call readBMPData f$ +"720x720.bmp"
wait

sub quit h$
    #w.te "!copy"   '   copy potential g-code file to clipboard ( or could save it)
    close #h$
    if BisOpen =1 then close #bmp
    if GisOpen =1 then close #gbd
    end
end sub

sub GetBmpDimensions fileName$, byref width, byref height
    temp$  =input$( #gbd, 24)
    close #gbd
    GisOpen =0
    width  =asc( mid$( temp$, 19, 1)) +asc( mid$( temp$, 20, 1)) *256
    height =asc( mid$( temp$, 23, 1)) +asc( mid$( temp$, 24, 1)) *256
end sub


sub readBMPData fileName$                         'reads into arrays r g b (w,h)
        open fileName$ for input  as #gbd
        GisOpen     =1
        open fileName$ for binary as #bmp
        BisOpen     =1
        lenFile     =lof( #bmp)
        info$       =input$( #bmp, 54)            'get bmp info
        bmpWidth    = value( mid$( info$, 19, 2)) 'extract bmp info -width in pixels of image
        bmpHeight   = value( mid$( info$, 23, 2)) '                  height in pixels of image
        bmpOffset   = value( mid$( info$, 11, 4)) 'offset in file to pixel data 54 or 66 for Liberty bmps
        bpp         = value( mid$( info$, 29, 2)) 'color depth, 32bit /4bytes or 24bit /3bytes
        bmpColDepth = bpp /8
        rasterWidth =bmpWidth *bmpColDepth        'work out padding, a raster must end on a 4 byte boundary
        p           =rasterWidth mod 4
        if p then rasterWidth =rasterWidth +( 4 -p)

        'print "Bitmap "; fileName$; " "; bmpWidth; " x "; bmpHeight; " "; bpp; " bpp"
        seek #bmp, bmpOffset                      'set pointer to first of color quad or triplet info

        if bpp <>24 and bpp <>32 then             'we'll accept only 24 or 32 bpp images so pixels will be RGB or RGB0
            print "bpp value is "; bpp
            print "Come on! JB generated bitmap supposed to be 24 or 32 bpp"
            print "* Refuse to continue! *"
            end
        end if

        'dimension array. Clears it too
        dim r( bmpWidth +1, bmpHeight +1), g( bmpWidth +1, bmpHeight +1), b( bmpWidth +1, bmpHeight +1)

        'print "Reading to array ... ";            'read the data
        c =0

        for y = 0 to bmpHeight -1
            aLine$ =input$( #bmp, rasterWidth)
            scan
            for x =0 to bmpWidth -1
                pixel$ =mid$( aLine$, x *bmpColDepth +1, bmpColDepth)
                'and y is reversed
                re =asc( mid$( pixel$, 3, 1)): r( x, bmpHeight -1 -y) =re     '   fill array with each pixel's RGB
                gr =asc( mid$( pixel$, 2, 1)): g( x, bmpHeight -1 -y) =gr
                bl =asc( mid$( pixel$, 1, 1)): b( x, bmpHeight -1 -y) =bl
                '#w.gb "color "; bl; " "; gr; " "; re                         '   colour-swapped false colours.

                grey =int( ( re +gr +bl) /3)
                if grey >140 then grey =255 else grey =0    '   threshold for black/white
                #w.gb "color "; grey; " "; grey; " "; grey
                #w.gb "set "; x; " "; bmpHeight -y
                #w.te "G00 X"; x; " Y"; y
                if grey >140 then #w.te "G=code for pendown/up"
            next
        next

    close #bmp
    BisOpen =0
    print "Done."
end sub

function value( x$)
    select case len( x$)
        case 1
            value = asc( x$)
        case 2
            value =asc( mid$( x$,1,1))
            value =value +( asc( mid$( x$, 2, 1)) *256)
        case 4
            value =asc( mid$( x$, 1, 1))
            value =value+( asc( mid$( x$, 2, 1)) *256)
            value =value+( asc( mid$( x$, 3, 1)) *256^2)
            value =value+( asc( mid$( x$, 4, 1)) *256^3)
    end select
end function

My webpages at Diga Me!- 'Talk to me!"