Welcome to the first unit of our course on building a complete panorama stitcher! In our previous course, we learned how to detect features, match them, and calculate a transformation to align two overlapping photos. That process left us with two output images: a warped_source and a target_canvas.
Right now, these two images are perfectly aligned, but they sit in two separate arrays on the exact same giant coordinate grid. Imagine two transparent plastic sheets stacked on top of each other, each with a piece of the puzzle. In this lesson, we are going to learn how to blend these two sheets together into a single, combined image array. Once they are combined, we will also slice away the large, empty black borders that the warping process left behind.
Before we can merge our two images, we need a way to tell our computer which parts of our giant canvas actually contain a photograph, and which parts are just empty, black background. We do this by creating image "masks." A mask is simply a map of True and False values.
In digital images, a completely black pixel has a value of [0, 0, 0] for its Blue, Green, and Red channels. If any of those channels is greater than zero, we know there is image data there. Let's create our masks.
By checking > 0 along axis=2 (which represents our three color channels), np.any creates a True/False map for each image. True means "there is a picture here," and False means "this is empty black space."
Now, we need to locate specific regions. We want to know where only the source image exists, and where both images overlap.
Here, we use the bitwise AND operator (&) and the bitwise NOT operator (~).
source_onlybecomesTrueonly where thesource_maskisTrueAND thetarget_maskisFalse.overlapbecomesTruewhere both masks areTrue.
Now that we have our masks, we can start pasting pixels onto a final result canvas. We will start by copying everything from our target_canvas.
Since our result array already has the target image data, we only need to add the source image data. First, let's copy over the pixels that exist only in the source image. We can use our source_only mask to select those specific pixels.
Next, we must handle the overlapping region where both images exist. For our simple stitcher, we will blend them by taking a 50/50 mathematical average.
Notice that we multiplied both arrays by 0.5 and added them together. Because multiplying turns our whole numbers into decimals (floats), we use .astype(np.uint8) to convert the values back into the standard 8-bit integers that OpenCV expects for images.
If you were to test this right now, you would likely notice a visible seam or line where the two images meet. This is completely normal! It means our math is working, but real-world cameras shift their lighting and exposure slightly between shots, making one side of the overlap slightly brighter or darker than the other.
Now, let's zoom out and look at how these two new functions fit into our main stitching pipeline. After we calculate the homography and warp our images, we get our two canvases.
Instead of dealing with them separately, we can now pass them directly into our new functions in our main script.
The output of our composite function acts as the direct input for our crop_black function. The final result variable now holds our clean, tightly cropped, two-image panorama. When you run this complete pipeline, the console output will summarize our hard work:
Great job! You now understand the final piece of the basic image stitching puzzle. In this lesson, we learned how to:
- Use
NumPyarrays to map out masks that differentiate our real photo from the empty black background. - Combine arrays using logical operators (
&,~) to isolate specific image regions. - Apply basic mathematical operations to average out overlapping areas.
- Locate extreme coordinates and slice arrays to cleanly crop an image.
You are now ready to tackle the upcoming interactive exercises. In those practices, you will get hands-on experience building these masking and compositing functions yourself. Good luck!
