#!/usr/bin/python

# number_bullet.py
#       --copyright--                   Copyright 2007 (C) Tranzoa, Co. All rights reserved.    Warranty: You're free and on your own here. This code is not necessarily up-to-date or of public quality.
#       --url--                         http://www.tranzoa.net/tzpython/
#       --email--                       pycode is the name to send to. tranzoa.com is the place to send to.
#       --bodstamps--
#       July 30, 2007           bar
#       July 31, 2007           bar
#       August 2, 2007          bar     get working with a bunch of test images of 4 test numbers, each
#       November 18, 2007       bar     turn on doxygen
#       November 27, 2007       bar     insert boilerplate copyright
#       May 17, 2008            bar     email adr
#       May 25, 2010            bar     tz_os_priority
#       November 29, 2011       bar     pyflake cleanup
#       --eodstamps--
##      \file
#
#
#       Create and read numbers represented graphically as bullseyes.
#
#       Todo:
#
#           To speed up the scan - especially in images with no number bullets:
#             Put a white ring as the second ring - that way _try_chord only needs to be called when there are balanced bw | wb pairs inside the outside wb-bw.
#
#           To make the recognition more resilent to noise (and as an extra check):
#             Put a black bulls eye in the center so that if the diameter is slightly off center, the error in the inner rings won't be as bad, and for fast rationality check.
#
#           Bullets cannot be hex-packed. (_in_fnd will filter them)
#
#           If, outside the bullets were filled with random double colors,
#             then histogram enhancment might be effective without skewing things.
#             (unless multiple bullets all looked nearly the same)
#             Might trigger a lot of high-level false positives.
#
#           Bullets can be double checked by looking at the x-diamenter, too.
#
#

import  copy

import  tzlib


BLACK_WHITE_OUTER_RING  = True


COLORS  = [
            [   0,   0,   0 ],
            [  32,  32, 255 ],
            [  32, 255,  32 ],
#           [   0, 255, 255 ],
            [ 255,  32,  32 ],
#           [ 255,   0, 255 ],
#           [ 255, 255,   0 ],
            [ 255, 255, 255 ],
          ]

BASE    = len(COLORS) - 1



BLACK_TO_WHITE      = 2.1

LIGHT_BLACK         = 85
DARK_WHITE          = 120


SHOW_WHITE_BLACKS   = False



def icolor(rgb) :
    return((rgb[0] << 16) | (rgb[1] << 8) | rgb[2])



def _zero_255(rgb) :
    r   = []
    for c in rgb :
        if  c <= 128 :
            c   = 0
        else :
            c   = 255
        r.append(c)
    return(r)



ICOLORS = [ icolor(_zero_255(rgb)) for rgb in COLORS ]

DCOLORS = tzlib.make_dictionary(ICOLORS)



def white_or_black(rgb) :
    mx  = max(rgb)
    mn  = min(rgb)
    return((mn >= DARK_WHITE) or (mx < LIGHT_BLACK) or ((mn * 12) / 7 > mx))


def different_shade(c1, c2) :
    return(((c2 >= DARK_WHITE) and (c1 * BLACK_TO_WHITE < c2)) or ((c1 >= DARK_WHITE) and (c2 * BLACK_TO_WHITE < c1)))


def different_colors(rgb1, rgb2) :
    return(different_shade(rgb1[0], rgb2[0]) or different_shade(rgb1[1], rgb2[1]) or different_shade(rgb1[2], rgb2[2]))



def color_to_pure_icolor(rgb) :

    mx  = max(rgb)
    mn  = min(rgb)

    if  white_or_black(rgb) :
        if  mx <= LIGHT_BLACK :
            return(0)
        if  mn >= DARK_WHITE :
            return(0xffffff)
        if  mn < ((255 - mx) * 2) / 3 :
            return(0)
        return(0xffffff)

    rc  = [ 255, 255, 255 ]
    if  abs(rgb[0] - mn) < abs(rgb[0] - mx) :
        rc[0]   = 0
    if  abs(rgb[1] - mn) < abs(rgb[1] - mx) :
        rc[1]   = 0
    if  abs(rgb[2] - mn) < abs(rgb[2] - mx) :
        rc[2]   = 0

    return(icolor(rc))



class   a_number_bullet :


    class   a_reversed_picture :

        def __init__(me, picture) :
            me.picture  = picture


        def draw_ring(me, ring, number_of_rings, rgb) :
            me.picture.draw_ring(ring, number_of_rings, rgb)


        def hite(me) :
            return(me.picture.width())


        def width(me) :
            return(me.picture.hite())

        def get_pixel(me, x, y) :
            return(me.picture.get_pixel(y, x))

        #   a_reversed_picture




    def __init__(me, highest_number) :

        me.high_n           = highest_number

        me.extra_rings      = 0
        me.rings            = 2             # white surrounding area and black outer ring
        if  BLACK_WHITE_OUTER_RING :
            me.rings       += 1             # white second ring
            me.extra_rings  = 1

        while highest_number :
            me.rings       += 1
            highest_number /= BASE

        pass



    def highest_number(me) :

        return(me.high_n)



    def black_and_white_number(me) :

        bwn = 0
        for i in xrange(me.rings - 2 - me.extra_rings) :
            bwn *= BASE
            bwn += (BASE - 1)

        return(bwn)



    def paint(me, painter, n) :
        """
            Call back to painter.draw_ring to paint a number bullet.
        """

        if  n > me.black_and_white_number() :
            return(False)

        ca  = copy.copy(COLORS)

        ci  = 0
        painter.draw_ring(0, me.rings, ca[-1])                  # draw a white circle around the whole thing so it's there for sure

        if  BLACK_WHITE_OUTER_RING :
            painter.draw_ring(1, me.rings, ca[ 0])              # draw a black circle
            ci  = len(ca) - 1                                   # and let the loop start with a white circle

        for i in xrange(me.extra_rings, me.rings - 1) :
            painter.draw_ring(i + 1, me.rings, ca[ci])
            (ca[0], ca[ci]) = (ca[ci], ca[0])

            ci              = (n % BASE) + 1
            n              /= BASE

        return(True)





    def _in_fnd(me, x, y) :
        for bb in me.fnd.values() :
            if  (bb[0] <= y <= bb[1]) and (bb[3] <= x <= bb[2]) :
                return(bb[2])
            pass

        for bb in me.ignores :
            if  (bb[0] <= y <= bb[1]) and (bb[3] <= x <= bb[2]) :
                return(bb[2])
            pass

        return(None)



    def _black_to_white_scan(me, x, y, ys, yd) :
        """
            Return an array of black to white transition points (the white point's y value, that is).
        """

        bwa         = []

        if  y >= 0  :
            yt          = y + (yd * ys)

            prgb        = me.picture.get_pixel(x, y)
            pbw         = white_or_black(prgb)
            ps          = sum(prgb)
            while True :
                ny      = y + (ys * me.step)
                if  not (0 <= ny < me.picture.hite()) :
                    break

                if  ys  > 0 :
                    if  ny >= yt :
                        break
                    pass
                elif    ny <= yt :
                    break

                rgb     = me.picture.get_pixel(x, ny)
                bw      = white_or_black(rgb)
                s       = sum(rgb)

                if  SHOW_WHITE_BLACKS and (x == 997) :
                    print "bws", x, y, "to", ny, prgb, rgb, pbw, bw, ps, s

                if  pbw and bw:
                    if  (ps * BLACK_TO_WHITE < s) and different_shade(ps / 3, s / 3) :      # black to white boundary?
                        if  bwa and (abs(bwa[-1] - ny) <= me.step) :
                            bwa[-1]     = ny
                        else :
                            bwa.append(ny)
                        pass
                    pass

                if  me.step == 1 :
                    y       = ny
                    prgb    = rgb
                    pbw     = bw
                    ps      = s
                else :
                    y      += ys
                    prgb    = me.picture.get_pixel(x, y)
                    pbw     = white_or_black(prgb)
                    ps      = sum(prgb)
                pass
            pass

        return(bwa)




    def _try_bullet(me, xf, xt, y) :
        """
            We have a bullet, now look the other direction to check it out.
        """


        xm  = (xt + xf) / 2
        xh  = (xt - xf) / 2

        d   = max(3, me.rings / 2, ((xt - xf) * 3) / me.rings)

        fa  = me._black_to_white_scan(xm, y - xh + d, -1, d * 2)
        ta  = me._black_to_white_scan(xm, y + xh - d,  1, d * 2)

        if  SHOW_WHITE_BLACKS :
            if  fa and ta :
                print "  fa ta    b", xf, xt, xm, y, d, fa, ta
            else :
                print "  no fa ta b", xf, xt, xm, y, d, xf + d, xf - d * 2, xt - d, xf + d * 2, fa, ta
            pass

        for f in fa :
            for t in ta :
                n   = me._try_diameter(xm, f, t, first = False)
                if  n  != None :

                    v   =  [ xf, xt, t, f, n ]
                    me.ignores.append(v)

                    return(v)

                pass
            pass

        return(None)






    def _try_diameter(me, x, yf, yt, first = True) :
        if  (yt - yf >= me.rings * 2) and ((not me.lfnd) or (me.lfnd_hl <= yt - yf <= me.lfnd_hh)) :

            # print "dia", x, yf, yt, me.rings


            #
            #   Handle when we are stepping more than 1 and the image is a pure, clean image
            #

            while yt - yf > me.rings * 2 :
                if  color_to_pure_icolor(me.picture.get_pixel(x, yf + 1)) != 0xffffff :
                    break
                yf += 1
                pass

            while yt - yf > me.rings * 2 :
                if  color_to_pure_icolor(me.picture.get_pixel(x, yt - 1)) != 0xffffff :
                    break
                yt -= 1
                pass

            if  (not me.lfnd) or (me.lfnd_hl <= yt - yf <= me.lfnd_hh) :

                m   = (yt + yf) / 2.0

                rs  = (yt - yf) / (me.rings - 2)            # -2 'cause we don't count the surrounding white ring and the inner eye ring

                #
                #   This search for the middle of the middle makes the ring-accuracy higher, and it compensates for a fat center "ring"
                #
                fm  = m
                c   = color_to_pure_icolor(me.picture.get_pixel(x, fm))
                while fm + rs > m :
                    nc  = color_to_pure_icolor(me.picture.get_pixel(x, fm - 1))
                    if  nc != c :
                        break

                    fm -= 1
                    c   = nc

                tm  = m
                c   = color_to_pure_icolor(me.picture.get_pixel(x, tm))
                while tm - rs < m :
                    nc  = color_to_pure_icolor(me.picture.get_pixel(x, tm + 1))
                    if  nc != c :
                        break

                    tm += 1
                    c   = nc

                om  = m
                m   = (tm + fm) / 2

                if  abs(m - om) / (tm - fm + 1) < 0.40 :

                    # print "doing bullet", x, yf, yt, int(fm), int(om), int(m), int(tm), int(abs((m - om) * 100.0) / (tm - fm + 1))

                    af  = (fm - yf) / float(me.rings - 2)
                    ff  =  yf + (af / 2.0)

                    at  = (yt - tm) / float(me.rings - 2)
                    ft  =  yt - (at / 2.0)

                    ra  = []
                    for r in xrange(me.rings - 1) :
                        cn      = color_to_pure_icolor(me.picture.get_pixel(x, int(ff)))
                        cs      = color_to_pure_icolor(me.picture.get_pixel(x, int(ft)))
                        if  False or (SHOW_WHITE_BLACKS and (yf == 300) and (yt == 1014)) :
                            print x, int(ff), int(ft), "%06x" % (cn) , me.picture.get_pixel(x, int(ff)), "%06x" % (cs), me.picture.get_pixel(x, int(ft)), yf, m, yt
                        if  cn != cs :
                            return(None)
                        ra.append(cn)
                        if  len(ra) >= 2 :
                            if  ra[-1] == ra[-2] :
                                return(None)
                            pass
                        ff     += af
                        ft     -= at


                    ra.reverse()
                    cn  = ra.pop()
                    if  cn :
                        return(None)                        # the outer ring better be black!

                    ca  = copy.copy(ICOLORS)

                    if  BLACK_WHITE_OUTER_RING :
                        cn  = ra.pop()
                        if  cn != 0xffffff :
                            return(None)                    # the next ring must be white!
                        (ca[0], ca[-1]) = (ca[-1], ca[0])

                    na  = []
                    while ra :
                        # print "%06x" % (ra[-1]),

                        try :
                            ci          = ca.index(ra.pop())
                        except ValueError :
                            return(None)

                        na.append(ci - 1)
                        (ca[0], ca[ci]) = (ca[ci], ca[0])

                    # print na

                    na.reverse()
                    n   = 0
                    for d in na :
                        n  *= BASE
                        n  += d


                    # print "got bullet", int(fm), int(om), int(m), int(tm), int(abs((m - om) * 100.0) / (tm - fm + 1))

                    if  first :

                        # print "sorta found", n

                        v           = [ yf, yt, x + ((yt - yf) / 2), (x - (yt - yf) / 2), n ]

                        me.ignores.append(v)

                        cp          = me.picture
                        me.picture  = a_number_bullet.a_reversed_picture(me.picture)
                        v           = me._try_bullet(yf, yt, x)
                        me.picture  = cp

                        if  v :
                            # print "  and the other way"
                            pass
                        else :
                            # print "  but not the other way", n
                            n       = None

                        pass

                    return(n)               # since _try_chord sets some direction-dependent stuff, we'll let the first, normally-oriented diameter be the found value

                pass
            pass


        return(None)



    def _try_chord(me, xf, xt, y) :
        """
            We might have a chord from xf,y to xt,y.
            Try all the possible diameter lines at right angles to it in the middle of the chord.
        """


        if  (xt - xf > max(me.rings / 4, me.mstp / 2)) and ((not me.lfnd) or (me.lfnd_wl / 3 <= xt - xf <= me.lfnd_wh)) :

            xm  = (xt + xf) / 2

            if  not me._in_fnd(xm, y) :

                fa  = me._black_to_white_scan(xm, y, -1, (xt - xf) * 4)
                ta  = me._black_to_white_scan(xm, y,  1, (xt - xf) * 4)

                if  SHOW_WHITE_BLACKS :
                    if  fa and ta :
                        print "  fa ta",    xf, xt, xm, y, fa, ta
                    else :
                        print "  no fa ta", xf, xt, xm, y, fa, ta
                    pass

                for f in fa :
                    for t in ta :
                        tx      = (((xm * me.picture.hite()) + f) * me.picture.hite()) + t
                        if  not me.tried.has_key(tx) :
                            me.tried[tx]    = True

                            if  not me._in_fnd(xm, (f + t) / 2) :
                                n   = me._try_diameter(xm, f, t)
                                if  n != None :
                                    v           = [ f, t, xm + ((t - f) / 2), (xm - (t - f) / 2), n ]
                                    k           = (((f + t) / 2) * me.picture.width()) + xm

                                    me.fnd[k]   = v

                                    me.lfnd     = v

                                    me.lfnd_wl  = min(me.lfnd_wl, (v[2] - v[3]) / 1.3)
                                    me.lfnd_wh  = max(me.lfnd_wh, (v[2] - v[3]) * 1.3)
                                    me.lfnd_hl  = min(me.lfnd_hl, (v[1] - v[0]) / 1.3)
                                    me.lfnd_hh  = max(me.lfnd_hh, (v[1] - v[0]) * 1.3)

                                    h           = ((t - f) / 2) - 2
                                    if  me.mstp :
                                        me.mstp = min(me.mstp, h)
                                    else :
                                        me.mstp = h

                                    # print "found ---------------", me.fnd[k]

                                    return(v)

                                pass
                            pass
                        pass
                    pass
                pass
            pass
        else :
            # print "_try_chord too small or big", me.lfnd, me.lfnd_wl, me.lfnd_wh, xt - xf, xt, xf
            pass

        return(None)




    def _check_white(me, x, y, xd) :

        ln  = me.rings / 4
        if  me.lfnd :
            ln  = int(max(ln, me.lfnd_hl / (me.rings * 4)))
        ln  = max(3, ln)

        cs  = sum(me.picture.get_pixel(x, y))

        r   = [ 0, 0 ]

        ok  = True
        cc  = cs
        e   = min(me.picture.hite(), y + ln)
        for i in xrange(y + 1, e - 1) :
            c       = sum(me.picture.get_pixel(x, i))
            if  c   < cc * 0.6 :                            # it's so low because of pixelization from LCD's
                ok  = False
                break
            # cc      = min(cc, (c + cc) / 2)
        if  ok :
            r[0]    = e - y


        ok  = True
        cc  = cs
        e   = max(-1, y - ln)
        for i in xrange(y, e, -1) :
            c       = sum(me.picture.get_pixel(x, i))
            if  c   < cc * 0.6 :
                ok  = False
                break
            # cc      = min(cc, (c + cc) / 2)
        if  ok :
            r[1]    = y - e

        ln  = 2
        if  me.lfnd :
            ln  = int(max(ln, me.lfnd_hl / (me.rings * 4)))
        ln  = max(3, ln)

        cc  = cs
        e   = max(-1, min(me.picture.width(), x + (xd * ln) + xd))
        for i in xrange(x + xd, e, xd) :
            c       = sum(me.picture.get_pixel(i, y))
            if  c   < cc * 0.6 :
                r   = [ 0, 0 ]
                break
            # cc      = min(cc, (c + cc) / 2)

        return(r)






    def _scan_line(me, y) :
        """
            Scan across the picture along one Y line.
        """

        ba          = []
        bwi         = 0

        x           = 0

        prgb        = me.picture.get_pixel(x, y)
        pbw         = white_or_black(prgb)
        ps          = sum(prgb)
        x          += 1
        while x < me.picture.width() - me.step :

            nx      = me._in_fnd(x, y)
            if  nx != None :
                x       = nx
                try :
                    prgb    = me.picture.get_pixel(x, y)
                except IndexError :
                    break                                   # one of the ignores or found bullets bled over to outside the image
                pbw     = white_or_black(prgb)
                ps      = sum(prgb)
            else :
                nx      = x + me.step
                rgb     = me.picture.get_pixel(nx, y)
                bw      = white_or_black(rgb)
                s       = sum(rgb)

                if  pbw and bw:
                    if  (s * BLACK_TO_WHITE < ps) and different_shade(ps / 3, s / 3) :      # white to black boundary?
                        if  (not ba) or (not ba[-1][0]) or (x - ba[-1][1] > me.step * 2) :
                            ba.append( [  True,   x ] )
                        pass
                    if  (ps * BLACK_TO_WHITE < s) and different_shade(ps / 3, s / 3) :      # black to white boundary?
                        if  (not ba) or      ba[-1][0]  or (x - ba[-1][1] > me.step * 2) :
                            ba.append( [ False,  nx ] )
                        else :
                            ba[-1][1]   = x                 # widen the b2w point out
                        bwi = len(ba)
                    pass

                if  me.step == 1 :
                    prgb    = rgb
                    pbw     = bw
                    ps      = s
                else :
                    prgb    = me.picture.get_pixel(x, y)
                    pbw     = white_or_black(prgb)
                    ps      = sum(prgb)
                pass

            x          += 1


        if  SHOW_WHITE_BLACKS and ba :
            print "ba", y, ba

        i   = 0
        while i < bwi :
            f   = ba[i]
            i  += 1
            if  f[0] :
                # print "x, y", f[1], y

                (fu, fd)  = me._check_white(f[1], y, -1)
                if  max(fu, fd) != 0 :

                    e       = min(len(ba), i + me.rings * 2)

                    j   = i
                    if  BLACK_WHITE_OUTER_RING :
                        while j < e :
                            if  not ba[j][0] :                              # find the black ring to white ring transition (should be next ba, but we'll go easy on 'em)
                                break
                            j  += 1
                            pass
                        while j < e :
                            if  ba[j][0] :                                  # find a possible white to outer-black-ring transition
                                break
                            j  += 1
                            pass
                        j  += 1

                    if  j >= e :
                        break

                    for j in xrange(j, e) :
                        t   = ba[j]
                        if  not t[0] :

                            ok          = True
                            if  BLACK_WHITE_OUTER_RING :
                                ix          = f[1]
                                tx          = t[1]
                                ok          = False
                                fi          = i
                                ti          = j - 1
                                while fi < ti :
                                    if  ba[fi][0] :
                                        fi += 1
                                    elif not ba[ti][0] :
                                        ti -= 1
                                    else :
                                        fl  = ba[fi][1] - ix
                                        tl  =             tx - ba[ti][1]
                                        if  fl  < tl :
                                            if  fl * 5 / 4 > tl :
                                                ok  = True
                                                break
                                            else :
                                                fi += 1
                                            break
                                        elif tl * 5 / 4 > fl :
                                            ok  = True
                                            break
                                        else :
                                            ti -= 1
                                        pass
                                    pass
                                pass

                            if  ok :
                                (tu, td)    = me._check_white(t[1], y, +1)
                                if  (tu and fu) or (td and fd) :
                                    # print tu, fu, td, fd
                                    if  me._try_chord(f[1], t[1], y) != None :
                                        i   = j
                                        break
                                    pass
                                pass
                            pass
                        pass

                    if  len(me.fnd) >= me.max_to_find :
                        break
                    pass
                pass
            pass

        pass




    def scan(me, picture, min_to_find = 1, max_to_find = None) :
        """
            Scan the given picture object for number bullets.
        """

        me.picture      = picture # a_number_bullet.a_reversed_picture(picture)

        min_to_find     = min_to_find or 1
        min_to_find     = max(min_to_find, 1)

        me.max_to_find  = max_to_find or 1000000000
        me.max_to_find  = max(me.max_to_find, 1, min_to_find)

        me.tried        = {}
        me.fnd          = {}
        me.ignores      = []

        me.lfnd         = None
        me.lfnd_wh      = me.lfnd_hh  = 0
        me.lfnd_wl      = me.picture.width()
        me.lfnd_hl      = me.picture.hite()

        me.mstp         = 0
        me.step         = max(1, (min(me.picture.width(), me.picture.hite()) / 246) | 1)
        while (me.step > 0) and (len(me.fnd) < me.max_to_find) :

            # print "step --------------------------- ", me.step

            #   First scan across the middle, then each half step, doing finer half steps until they are too fine.
            #   It might be best to scan 1/4, 3/4, 1/2 in that order the first pass.

            ys      = me.picture.hite()
            while ys > max(me.rings - 2, me.mstp, (me.step * me.rings) / 2) :


                y       = ys / 2
                while y < me.picture.hite() :

                    # print "  scan line ---------- ", y, ys
                    me._scan_line(y)

                    if  len(me.fnd) >= me.max_to_find :
                        break

                    y  += ys

                ys     /= 2

            if  len(me.fnd) >= min_to_find :
                break

            me.step    -= 2


        return( [ bb[4] for bb in me.fnd.values() ] )



    pass        # a_number_bullet




if  __name__ == '__main__' :

    import  glob
    import  re
    import  sys

    import  Image
    import  ImageDraw
    import  ImageEnhance
    import  ImageFilter

    import  TZCommandLineAtFile
    import  tz_os_priority


    program_name        = sys.argv.pop(0)
    TZCommandLineAtFile.expand_at_sign_command_line_files(sys.argv)

    width   = 960
    hite    = 480

    all     = none  = False
    resize  = False

    while True :
        oi  = tzlib.array_find(sys.argv, [ "--all", "-a" ] )
        if  oi < 0 :    break
        del sys.argv[oi]
        all     = True


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--none", "-n" ] )
        if  oi < 0 :    break
        del sys.argv[oi]
        none    = True


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--white", "-w" ] )
        if  oi < 0 :    break
        del sys.argv[oi]

        BLACK_WHITE_OUTER_RING  = True

    while True :
        oi  = tzlib.array_find(sys.argv, [ "--not_white", "-n" ] )
        if  oi < 0 :    break
        del sys.argv[oi]

        BLACK_WHITE_OUTER_RING  = False


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--resize", "-z" ] )
        if  oi < 0 :    break
        del sys.argv[oi]
        resize      = True


    class   a_td :
        def __init__(me, ii) :
            me.ii   = ii
            me.im   = me.ii.load()
            me.d    = None

            (me._width, me._hite)   = me.ii.size


        def width(me) :
            return(me._width)

        def hite(me) :
            return(me._hite)


        def draw_ring(me, ring, of_rings, rgb) :
            if  not me.d :
                me.d    = ImageDraw.Draw(me.ii)
            r           = int(round(me.r - (ring * (float(me.r) / of_rings))))
            me.d.ellipse([ me.x - r, me.y - r, me.x + r, me.y + r ], outline = tuple(rgb), fill = tuple(rgb))


        def draw_text(me, x, y, s) :
            if  not me.d :
                me.d    = ImageDraw.Draw(me.ii)
            me.d.text( [ x, y ], s, fill = tuple( [ 0xff, 0x00, 0xff ] ) )


        def get_pixel(me, x, y) :
            return(me.im[x, y])

        pass


    brr     = a_number_bullet(3600 * 24 * 10) # 1048575)





    def _paint(ptr, brr, n, x, y) :
        ptr.r   = 110
        ptr.x   = x
        ptr.y   = y
        dd  = n
        while True :
            print   dd  % BASE,
            dd         /= BASE
            if  not dd :
                break
            pass
        print n

        (w, h)  = ptr.ii.size
        if  y >= h / 2 :
            ptr.draw_text(x - ptr.r, y + ptr.r - 10, str(n))
        else :
            ptr.draw_text(x - ptr.r, y - ptr.r, str(n))

        brr.paint(ptr, n)


    if  sys.argv :

        tz_os_priority.set_proc_to_idle_priority()

        while sys.argv :
            fn      = sys.argv.pop(0)
            if  re.match(r"^\d+$", fn) :
                ii  = Image.new("RGB", [ width, hite ], 0xffffff)
                ptr = a_td(ii)

                _paint(ptr, brr, int(fn), 120, 120)

                ii.save(fn + ".png")

                ptr = a_td(ii)
                na  = brr.scan(ptr)
                na.sort()
                print na

            else :

                fns     = glob.glob(fn)
                for fn in fns :
                    ii  = Image.open(fn)
                    print fn

                    if  False :                                 # takes a couple of seconds or more
                        e   = ImageEnhance.Sharpness(ii)
                        e.degenerate    =   ii.filter(ImageFilter.MedianFilter(3))
                        ii  = e.enhance(0.0)

                    if  True :
                        ( w, h )    = ii.size
                        if  resize or (min(w, h) >= 2000) :     # takes too long if the image is ok, but sure makes it go faster if the image is bogus
                            if  w < h :
                                ii  = ii.resize( [ 640, int(h * (640.0 / w))      ], Image.ANTIALIAS)
                            else :
                                ii  = ii.resize( [      int(h * (640.0 / h)), 640 ], Image.ANTIALIAS)
                            pass
                        pass

                    if  False :
                        e   = ImageEnhance.Sharpness(ii)
                        ii  = e.enhance(0.1)

                        e   = ImageEnhance.Sharpness(ii)
                        ii  = e.enhance(2.0)

                    ptr = a_td(ii)
                    na  = brr.scan(ptr, min_to_find = 1, max_to_find = 8)
                    na.sort()
                    print na

                    if  none and na :
                        break
                    if  all and not na :
                        break
                    pass
                pass
            pass
        pass

    else :

        import  random

        ii  = Image.new("RGB", [ width, hite ], 0xffffff)
        ptr = a_td(ii)

        _paint(ptr, brr, 123456,                                  120, 120)
        _paint(ptr, brr,      0,                                  360, 120)
        _paint(ptr, brr, 987654,                                  600, 120)
        _paint(ptr, brr, 999999,                                  840, 120)
        _paint(ptr, brr, brr.highest_number(),                    120, 360)
        _paint(ptr, brr, brr.black_and_white_number(),            360, 360)
        _paint(ptr, brr, random.randint(0, brr.highest_number()), 600, 360)
        _paint(ptr, brr, random.randint(0, brr.highest_number()), 840, 360)


        ii.save("x.png")

        ptr = a_td(ii)
        na  = brr.scan(ptr)
        na.sort()
        print na

    pass


#
#
#
# eof

