#---------------------------------------------------------------------- # Fleshed-out command processor for image processing. # # (C) Copyright March 23, 2018 -- Dr. William T. Verts # # Global variable Offset was created to allow Brighten_Pixel and # Darken_Pixel to be run from Process, but yet still allow the user # to specify how much each pixel should be brightened or darkened. # Offset is global to Brighten_Pixel, Darken_Pixel, and Main. # # Functions RotateRight and RotateLeft included, but they require a # new way of working with canvases. All previous functions modify a # canvas in-place, but because these new functions result in a canvas # where width and height are reversed they must create and return a # new canvas with the correct dimensions. #---------------------------------------------------------------------- import string #---------------------------------------------------------------------- # Here most of the functions do something to a pixel, but that is it. # The double-loop, getPixel, and repaint have been factored out into # the Process function, which in order to work must be passed as a # parameter (FN) the actual pixel function to be applied. #---------------------------------------------------------------------- Offset = 20 # Global to Brighten_Pixel, Darken_Pixel, and Main def Brighten_Pixel(PX): global Offset setRed (PX, getRed (PX)+Offset) setGreen(PX, getGreen(PX)+Offset) setBlue (PX, getBlue (PX)+Offset) return def Darken_Pixel(PX): global Offset setRed (PX, getRed (PX)-Offset) setGreen(PX, getGreen(PX)-Offset) setBlue (PX, getBlue (PX)-Offset) return def Monochrome_Pixel(PX): Average = (getRed(PX) + getGreen(PX) + getBlue(PX)) / 3 if Average > 128: setColor(PX,white) else: setColor(PX,black) return def Negate_Pixel(PX): setRed (PX, 255 - getRed(PX)) setGreen(PX, 255 - getGreen(PX)) setBlue (PX, 255 - getBlue(PX)) return def Gray_Pixel(PX): Average = (getRed(PX) + getGreen(PX) + getBlue(PX)) / 3 setRed (PX,Average) setGreen(PX,Average) setBlue (PX,Average) return def Process (Canvas, FN): for Y in range(getHeight(Canvas)): for X in range(getWidth(Canvas)): FN(getPixel(Canvas,X,Y)) repaint(Canvas) return #---------------------------------------------------------------------- # Functions that have to be stand-alone, and cannot be used with # Process. Flip and Mirror have to deal with pairs of pixels at # a time. # # Question: In Mirror, what happens to the middle pixel of each row # when the canvas is an odd number of pixels wide? # Is it ignored, or is it swapped with itself? # # Question: Can we factor out the code to swap pixels? It'll be the # same in both Flip and Mirror. #---------------------------------------------------------------------- def SwapPixels (PX1,PX2): Color1 = getColor(PX1) Color2 = getColor(PX2) setColor(PX1, Color2) setColor(PX2, Color1) return def Mirror (Canvas): W = getWidth(Canvas) H = getHeight(Canvas) for Y in range(H): for X in range(W/2): SwapPixels(getPixel(Canvas,X,Y),getPixel(Canvas,W-1-X,Y)) repaint(Canvas) return def Flip (Canvas): W = getWidth(Canvas) H = getHeight(Canvas) for X in range(W): for Y in range(H/2): SwapPixels(getPixel(Canvas,X,Y),getPixel(Canvas,X,H-1-Y)) repaint(Canvas) return #---------------------------------------------------------------------- # Rotating an image results in a new canvas where width and height are # reversed. Therefore, there must be a new canvas created and returned # by the function to where it was called. The new canvas will be # assigned to the primary canvas in Main. #---------------------------------------------------------------------- def RotateRight (Canvas): W = getWidth(Canvas) H = getHeight(Canvas) NewCanvas = makeEmptyPicture(H,W) for Y in range(H): for X in range(W): PX1 = getPixel(Canvas,X,Y) PX2 = getPixel(NewCanvas,H-1-Y,X) setColor(PX2,getColor(PX1)) repaint(NewCanvas) return NewCanvas def RotateLeft (Canvas): W = getWidth(Canvas) H = getHeight(Canvas) NewCanvas = makeEmptyPicture(H,W) for Y in range(H): for X in range(W): PX1 = getPixel(Canvas,X,Y) PX2 = getPixel(NewCanvas,H-1-Y,X) # The new X and Y are wrong (copied from RotateRight). What should they be? setColor(PX2,getColor(PX1)) repaint(NewCanvas) return NewCanvas #---------------------------------------------------------------------- # The updated command processor. Notice that GrayScale still uses # the explicit function, and hasn't yet been updated to the new # style using Process. All the other image commands call Process, # but must tell Process which pixel function to use as parameter FN. #---------------------------------------------------------------------- def Main(): global Offset Message = """Enter a Command from the list: QUIT OPEN OFFSET SAVE BRIGHTEN DARKEN MONO GRAY NEGATE MIRROR FLIP FOCUS EMBOSS CONTRAST ROTATELEFT ROTATERIGHT Enter a command --- """ Canvas = makeEmptyPicture(100,100) MoreToDo = True while (MoreToDo): repaint(Canvas) S = string.upper(requestString(Message)) print S if (S == "QUIT" ): MoreToDo = False elif (S == "OPEN" ): Filename = pickAFile() Canvas = makePicture(Filename) elif (S == "OFFSET" ): Offset = requestInteger("Enter Offset for Brighten/Darken") elif (S == "SAVE" ): pass elif (S == "BRIGHTEN" ): Process(Canvas, Brighten_Pixel) # These functions elif (S == "DARKEN" ): Process(Canvas, Darken_Pixel) # all modify the elif (S == "MONO" ): Process(Canvas, Monochrome_Pixel) # canvas in-place, elif (S == "NEGATE" ): Process(Canvas, Negate_Pixel) # and do not elif (S == "GRAY" ): Process(Canvas, Gray_Pixel) # return any elif (S == "MIRROR" ): Mirror(Canvas) # value to where elif (S == "FLIP" ): Flip(Canvas) # they were called. elif (S == "ROTATELEFT" ): Canvas = RotateLeft(Canvas) # These functions return elif (S == "ROTATERIGHT"): Canvas = RotateRight(Canvas) # a new canvas, overwriting the old else: showError("ERROR, " + S + " is not a legal command") return