Myro Picture Examples

From IPRE Wiki
Jump to: navigation, search

Below are a few functions for manipulating Myro Picture objects. Some also make use of Zelle's GraphWin objects, or convert between these two types of picture objects.


A function to reflect an image:

def reflectFromLeft(pic):
    '''Reflects pic (by copying a reversed image of the left
    hand side to the right hand side)

    pic     a Myro Picture object
    '''
    width = getWidth(pic)
    
    #copy left hand side of pic to right hand side, reversed
    for p in getPixels(pic):
        x = getX(p)
        y = getY(p)
        if x < width/2.: #left hand side of picture only
            setPixel(pic, width - x - 1, y, getColor(p))
    show(pic)
    return pic


A function to copy part of one image onto another:

def changeIt(pic, other):
    ''' Changes a portion of 'pic' to the pixels in 'other'.

    pic     a Myro Picture object
    '''
    pixels = []
    for i in range(10,20):
        for j in range(2, 10):  
            pix = getPixel(pic, i, j)
            setPixel( pic, i, j, getColor(getPixel(other, i, j)) )

    show(pic)


A function to copy a Myro Picture object to a Zelle GraphWin object:

    
def copyIt(pic, name = "Copy of pic", w = 400, h = 400):
    '''Copies Myro Picture object to a GraphWin.
    This function takes about 10 sec for 400 x 400 picture.

    pic     a Myro Picture object
    name    string to display as title for GraphWin
    w, h    desired width and height of GraphWin'''
    newWin = GraphWin(name, w, h)
    for p in getPixels(pic):
        
        #get coords & color info of orig pic
        x = getX(p)
        y = getY(p)

        #only plot relevant pixels (ones that can be seen
        #w/in the newWin)
        if x > w and y > h: break
        
        r, g, b = getRGB(p)

        #Set coords & color info for new window
        newWin.plot(x, y, color_rgb(r, g, b))
    return newWin

A class called CropLine a Zelle Graphics Line that can be moved based on gamepad input is shown below. Unlike Zelle's Graphics objects, this takes a specific GraphWin as input. Then, based on the line's orientation (horizontal or vertical), line movement is restricted to the GraphWin. Example use is in selecting the margins of a picture to copy, as shown further below.

class CropLine(Line):
    '''A Zelle Graphics Line that can be moved with gamepad
    input.  Permissible directions of movement are determined
    by an 'orientation' attribute (self.orient).'''
    
    def __init__(self, x, y, x2, y2, win, name = 'A Crop Line', orient = None):
        Line.__init__(self, Point(x, y), Point(x2, y2) )
        self.win = win
        self.name = name
        if orient == None:
            dx = x2 - x
            dy = y2 - y
            if dx == 0 and dy != 0:
                orient = 'vert'
            elif dy == 0:
                orient = 'horiz'
            else:
                orient = 'weird'
        self.orient = orient

    def moveItMouse(self):
        '''Moves cropline based on mouse input.
        Due to issues with mouse-clicks, this method
        is not recommended for use.
        '''
        click = self.win.getMouse()
        self._changePos(click.getX(), click.getY())

    def getRightmost(self):
        '''Returns value of right-most pixel.'''
        return max(self.p1.getX(), self.p2.getX())
    
    def getLeftmost(self):
        '''Returns value of left-most pixel.'''
        return min(self.p1.getX(), self.p2.getX())
    
    def getTopmost(self):
        '''Returns value of top-most pixel.'''
        return max(self.p1.getY(), self.p2.getY())
    
    def getBotmost(self):
        '''Returns value of bottom-most pixel.'''
        return min(self.p1.getY(), self.p2.getY())
    
    def lineMove(self):
        '''Moves a cropline, or actually any other Zelle graphics
        object, based on gamepad input.'''

        prev = getGamepadNow('button')[:4]
        movedX, movedY = 0, 0
        count = 0
        waitFactor = 100
        amt = [0,0] #initialize
        
        visible = 3
        maxHeight = self.win.getHeight() - visible
        minHeight = visible
        maxWidth = self.win.getWidth() - visible
        minWidth = visible

        movementFor = {3: (0, 1), #up
                       0: (0, -1), #down
                       1: (-1, 0), #left
                       2: (1, 0) #right
                       }
        #if direction is vertical, do not allow up-down movement
        if self.orient == 'vert':
            movementFor[0] = (0, 0)
            movementFor[3] = (0, 0)

            
        #if direction is horizontal, do not allow left-right movement
        elif self.orient == 'horiz':
            movementFor[1] = (0, 0)
            movementFor[2] = (0, 0)

            
        while 1:
            if (  [round(pos) for pos in getGamepadNow('axis')]
                  != [0,0]  ): break #pressed axis
            curr = getGamepadNow('button')[:4]
            if curr != prev:
                for button in range(4):
                    #if now pressed, start moving
                    if curr[button] > prev[button]:
                        #amt[button] = amtRef[button]
                        if count % waitFactor == 0:
                            amt = movementFor[button]
                            moveAmt = list(amt) #can mutate superficially
                            #orig = copy(amt) #for reset
                            
                            #don't allow to move off the screen
                            if self.getRightmost() >= maxWidth:
                                moveAmt[0] = -abs(moveAmt[0]) #can only move left
                            if self.getLeftmost() <= minWidth:
                                moveAmt[0] = abs(moveAmt[0]) #can only move right
                            if self.getTopmost() >= maxHeight:
                                moveAmt[1] = -abs(moveAmt[1]) #can only move down
                            if self.getBotmost() <= minHeight:
                                moveAmt[1] = abs(moveAmt[1]) #can only move up                               
                            self.move(*moveAmt)
                            movedX += amt[0]
                            movedY += amt[1]
                            
                            #amt = copy(orig) #reset amt for continuing movement
                    else: pass #don't move
                            
            #enable waiting w/o gettting less information
            if count == waitFactor:
                count = 0
            count += 1
        
    def _changePos(self, newX, newY):
        '''Moves self _to_ position (newX, newY).
        Based on 'self.orient', only allows certain directions
        of movement.

        Note the difference here between this method and
        move, which moves the object _by_ the amount (dx, dy).
        
        For use with method moveItMouse.'''
        if self.orient == 'vert': #change x only
            changeX = newX - self.getP1().getX()
                #i.e. newX - x; x & x2 should be the same
            changeY = 0
        elif self.orient == 'horiz': #change y only
            changeX = 0
            changeY = newY - self.getP1().getY()
                #i.e. newY - y; y & y2 should be the same
        else: #don't change
            changeX = 0
            changeY = 0
        print 'cX, cY:', changeX, changeY
        self.move(changeX, changeY)
            

Example use of the CropLine -- allows user to select a portion of picture (cropping itself not yet implemented):

def getCropping(personFN):
    '''Allows user to select a rectangular portion of an image,
    using gamepad input.  Returns coordinates of lower left and
    upper right points of user-selected portion of image.
    
    Notes:
    1. Cropping itself is not yet implemented.
    2. Idea is to select only the face of a person in a
    picture.

    personFN    filename of an image (JPG) to crop
    '''

    person = makePicture(personFN)
    
    #copy picture from myro Picture object to Zelle's GraphWin object
    face = copyItTester(person, scale = 1) #.05, .01, .005

    # set up croplines
    w = getWidth(face)
    h = getHeight(face)
    coords = (0, 0, w, h)
    face.setCoords(*coords)
    translate = 10
    offset = 5
    vertlineL = CropLine(0 + offset, 0, 0 + offset, h, face, name = 'left vertical line')
    vertlineR = CropLine(w - offset + 1, 0, w - offset + 1, h, face, name = 'right vertical line')
    horizTop = CropLine(0, h - offset, w, h - offset, face, name = 'top horizontal line')
    horizBot = CropLine(0, 0 + offset, w, 0 + offset, face, name = 'bottom horizontal line')
    
    lines = [vertlineL, vertlineR, horizTop, horizBot]
    print "Please crop the image to your liking:\n"

    
    minXs = []
    maxXs = []
    minYs = []
    maxYs = []

    #have user select portion
    for line in lines:
        print "Using the gamepad, choose the location for the %s." %line.name
        line.setFill('pink')
        line.draw(face)
        
        print "Press any axis key on the gamepad when you are done."
        
        line.lineMove()
        wait(.5) #extremely important to allow time user holds down button

        #Store x and y positions for later
        if line.orient == 'vert':
            minXs.append( line.getLeftmost() )
            maxXs.append( line.getRightmost() )
        elif line.orient == 'horiz':
            minYs.append( line.getBotmost() )
            maxYs.append( line.getTopmost() )

    #Calculate x and y positions for cropped image
    minX = min(minXs)
    maxX = max(maxXs)
    minY = min(minYs)
    maxY = max(maxYs)

    return minX, minY, maxX, maxY, face

Use getCropping(personFN) like:

filename = "C:\Documents and Settings\Natasha Eilbert\Desktop\Natasha's Python Programs\\robotPic_14_hi.jpg"

minX, minY, maxX, maxY, face = getCropping(filename)

#check this works correctly, by displaying lower left
#and upper right points on picture
pMin = Point(minX, minY)
pMax = Point(maxX, maxY)
print "MIN (x,y): (%d, %d)" %(minX, minY)
print "MAX (x,y): (%d, %d)" %(maxX, maxY)
pMin.draw(face)
pMax.draw(face)