Masking a copy operation

As part of the previous chapter's work, we wrote copyRect() as a copy operation that limits itself to given rectangles of the source and destination images. Now, we want to apply further limits to this copy operation. We want to use a given mask that has the same dimensions as the source rectangle. We shall copy only those pixels in the source rectangle where the mask's value is not zero. Other pixels shall retain their old values from the destination image. This logic, with an array of conditions and two arrays of possible output values, can be expressed concisely with the numpy.where() function that we have recently learned.

Let's open rects.py and edit copyRect() to add a new argument, mask. This argument may be None, in which case we fall back to our old implementation of the copy operation. Otherwise, we next ensure that mask and the images have the same number of channels. We assume that mask has one channel but the images may have three channels (BGR). We can add duplicate channels to mask using the repeat() and reshape() methods of numpy.array. Finally, we perform the copy operation using where(). The complete implementation is as follows:

def copyRect(src, dst, srcRect, dstRect, mask = None,
             interpolation = cv2.INTER_LINEAR):
    """Copy part of the source to part of the destination."""
    
    x0, y0, w0, h0 = srcRect
    x1, y1, w1, h1 = dstRect
    
    # Resize the contents of the source sub-rectangle.
    # Put the result in the destination sub-rectangle.
    if mask is None:
        dst[y1:y1+h1, x1:x1+w1] = 
            cv2.resize(src[y0:y0+h0, x0:x0+w0], (w1, h1),
                       interpolation = interpolation)
    else:
        if not utils.isGray(src):
            # Convert the mask to 3 channels, like the image.
            mask = mask.repeat(3).reshape(h0, w0, 3)
        # Perform the copy, with the mask applied.
        dst[y1:y1+h1, x1:x1+w1] = 
            numpy.where(cv2.resize(mask, (w1, h1),
                                   interpolation = 
                                   cv2.INTER_NEAREST),
                        cv2.resize(src[y0:y0+h0, x0:x0+w0], (w1, h1),
                                   interpolation = interpolation),
                        dst[y1:y1+h1, x1:x1+w1])

We also need to modify our swapRects() function, which uses copyRect() to perform a circular swap of a list of rectangular regions. The modifications to swapRects() are quite simple. We just need to add a new argument, masks, which is a list of masks whose elements are passed to the respective copyRect() calls. If the given masks is None, we pass None to every copyRect() call. The following is the full implementation:

def swapRects(src, dst, rects, masks = None,
              interpolation = cv2.INTER_LINEAR):
    """Copy the source with two or more sub-rectangles swapped."""
    
    if dst is not src:
        dst[:] = src
    
    numRects = len(rects)
    if numRects < 2:
        return
    
    if masks is None:
        masks = [None] * numRects
    
    # Copy the contents of the last rectangle into temporary storage.
    x, y, w, h = rects[numRects - 1]
    temp = src[y:y+h, x:x+w].copy()
    
    # Copy the contents of each rectangle into the next.
    i = numRects - 2
    while i >= 0:
        copyRect(src, dst, rects[i], rects[i+1], masks[i],
                 interpolation)
        i -= 1
    
    # Copy the temporarily stored content into the first rectangle.
    copyRect(temp, dst, (0, 0, w, h), rects[0], masks[numRects - 1],
             interpolation)

Note that the mask in copyRect() and masks in swapRects() both default to None. Thus, our new versions of these functions are backward-compatible with our previous versions of Cameo.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset