Lab: Copying a Picture Into a Bigger Picture And Scaling Pictures

 


Introduction

The objective of this lab is to learn how to perform more complicated image manipulations by copying a picture into another picture, and scaling a picture.



Part 1: Copying a picture into a bigger picture

  1. The following unfinished function, copyInto, takes as its input a picture to be copied, a blank canvas (an empty picture) in which to copy it, and the upper left x and y coordinates of the location on the canvas where you want to copy the picture.
    
    def copyInto(picture, canvas, upperLeftX, upperLeftY):
      # Assign values to the variables widthToCopy and heightToCopy  
      # based on whether or not there is room on the canvas to fit the
      # entire picture.  If there isn't enough room, we should copy less
      # than the full width and height of the picture.
    
      #canvas argument will not be modified...      
      newCanvas = duplicatePicture(canvas)
    
      pictureWidth = getWidth(picture)
      pictureHeight = getHeight(picture)
      canvasWidth = getWidth(newCanvas)
      canvasHeight = getHeight(newCanvas)
    
      #These two lines only work if the picture doesn't spill off the canvas...
      widthToCopy = pictureWidth
      heightToCopy = pictureHeight
          
      #You need something like the following two lines instead.  
      #(You'll need to replace the question marks.)
      #widthToCopy = min(pictureWidth, ???????????)
      #heightToCopy = min(pictureHeight, ??????????)
    
      # Copy however much of the picture as will fit over to the canvas edge
      targetX = upperLeftX
      for sourceX in range(widthToCopy):
        targetY = upperLeftY
        for sourceY in range(heightToCopy):
          # Copy the color of the pixel at the source location to the 
          # destination location.
          sourcePix = getPixel(picture, sourceX, sourceY)
          color = getColor(sourcePix)
          destPix = getPixel(newCanvas, targetX, targetY)
          setColor(destPix, color)
          targetY = targetY + 1
        targetX = targetX +1
      return newCanvas
    
    
    The function as written so far only works if there is enough room on the canvas to copy the entire picture starting at the location you specify, as in the first illustration below. This is not always a safe assumption. The picture might be bigger than the canvas in at least one dimension, as in the second illustration, or it might be small enough to fit in the canvas but not when the copying starts at the specified upper-left corner, as in the third illustration. (In the illustrations, the black area represents the canvas in which we wish to copy the picture; any part of the picture covered with grid lines cannot be copied into the canvas.)
    Design Questions: How can you determine how much room is available in the canvas if you start copying the picture at location (upperLeftX, upperLeftY)? In other words, how can you determine the width of the space available from the specified upper left corner to the edge of the canvas? How can you determine the height of the space available from the same location?
    Analysis Questions: Must the canvas be a blank empty picture? Could you also use this function to copy a picture onto another (non-blank) picture?
    Modify the function so that it only copies as much of the original or source picture as there is room for. To do this, use the min function to find the smaller of the two values - the width of the picture, and the space available on the canvas. Modify the widthToCopy variable to refer to this value. (Alternatively, you could use an if statement to do this: compare the width of the picture with the space available on the canvas. If the space available is smaller than the picture width, modify the widthToCopy variable to refer to the remaining width of the canvas instead.) Do something similar for heightToCopy. After you have adjusted the width and height (if needed), the code above will copy the picture (or however much will fit) to the canvas.

    Make sure to comment your function appropriately.

    [picture that fits in canvas] [picture that is larger than canvas in one direction] [picture that is smaller than canvas but too close to edge]
    Picture fits in canvas Picture does not
    fit in canvas
    Picture could fit in
    canvas, but overhangs it

  2. Write a new matte function that takes two parameters, a picture and a matte width, and creates a new picture consisting of the original picture surrounded by a matte of the appropriate width.  (A matte is the sheet of cardboard or thick paper that often forms a border around a picture inside a frame.)  The function should return the new picture with the matte.  You may write your function so that it always creates a black matte, as in the examples below, or, if you have time, you may wish to add an extra parameter that specifies the matte color.
    Design Questions: Do you have other functions you can use to make writing this function easier?   If you have the original picture and know the width of the matte on all sides of the picture, how should you compute the dimensions of the new picture with the matte?  Where should the original picture be placed in the new picture?  How should you specify that?

    [a picture surrounded by a black matte] [a picture surrounded by a black matte] [a picture surrounded by a black matte]

  3. Sometimes pictures are framed with a wide matte and a second, much thinner matte of a contrasting color immediately around the picture. If you have time, you may want to try creating a double-matte effect.

    [a picture with a double-matte]


Part 2: Scaling and Tiling

  1. The following function scales a picture down to one-quarter of its original size. It uses every other pixel of the original picture. Copy this function and test that it works the way you would expect.
    
    # Returns a new picture 1/4 the size of the 
    # original picture (half as wide and half as high). 
    def quarter(orig):
      #Get the original width and height and divide them in half
      newWidth = getWidth(orig)/2
      newHeight = getHeight(orig)/2
    
      #Make an empty picture to store the scaled image
      newCanvas = makeEmptyPicture(newWidth,newHeight)
    
      #Copy every other pixel onto the new canvas
      for targetX in range(newWidth):
        for targetY in range(newHeight):
          color = getColor(getPixel(orig, targetX*2, targetY*2))
          setColor(getPixel(newCanvas, targetX, targetY), color)
      return newCanvas
    
  2. This next function will tile a picture onto a canvas as many times as it can. Copy this function and test it with pictures and canvases of different sizes.
    # Fills a canvas with as many copies of a picture as will
    # fit, including partial copies of the picture along the
    # right and lower edges if necessary
    def tile(picture, canvas):
      for targetY in range(0, getHeight(canvas),getHeight(picture)):
        for targetX in range(0, getWidth(canvas), getWidth(picture)):
          canvas = copyInto(picture, canvas, targetX, targetY)
      return canvas
    
  3. Write a function, tileWithQuarters, that takes a picture as a parameter. It should create a new picture the same size as the original picture, and tile four copies of the original image, scaled to 1/4-size. This function should return the new picture. (Hint: There are at least two ways to do this - you may use your tile function or you may think about how to use the copyInto function to do this.)

Submit your results

  1. Submit the file containing your functions via Kit.
  2. Submit one matted image and one image obtained from tileWithQuarter on Kit.