Touching Up An Old Photo With OpenCV

So, there is this old picture which has, among other people, my great-grandfather and his father. To be honest, a lot of the context for this picture is lost now; they all have rifles, but their clothing varies quite a bit. Some seem to be dressed rather fancy, others more frontiersy. In fact, one wonders if they made it a point to have each person wearing a different style hat? In any case, I asked my brother if he had a scan of it. He sent me one, but it was too big to fit in his scanner, so he had to do it in three sections and then manually align them back into one file. Here it is:

Province Brothers, Merged

So, not bad for manually adjusting, but he also sent the three original pieces, so I thought I would see if I could do better using OpenCV.


import cv2
from matplotlib import pyplot as plt


image_paths        = ["images/ProvinceBrothersLeft.jpg","images/ProvinceBrothersCenter.jpg","images/ProvinceBrothersRight.jpg"]
manual_stitch      = cv2.imread("images/ProvinceBrothersMerged.jpg", cv2.IMREAD_GRAYSCALE)

plt.subplot(2,3,4).set_title("Manual")
plt.imshow(manual_stitch, cmap="gray",  interpolation="nearest")

imgs = []
for i,image_path in enumerate(image_paths):
    img  = cv2.imread(image_path)
    imgs.append(img)
    plt.subplot(2,3,i+1)
    plt.imshow(img, cmap="gray",  interpolation="nearest")

stitcher=cv2.Stitcher.create()
(dummy,output)     = stitcher.stitch(imgs)

if dummy != cv2.STITCHER_OK:
    print("stitching process unsuccessful")
else:
    plt.subplot(2,3,5).set_title("Stitched")
    plt.imshow(output, cmap="gray", interpolation="nearest")
    cv2.imwrite("images/ProvinceBrothersStitched.jpg",output)
    plt.show()

The result looked like this; click the button to compare manually aligned vs "stitched" by OpenCV:

Stitched By OpenCV
Province Brothers, Stitched

If we zoom in on a section of the "seam" in the manually aligned images, we can see the difference even better. On the left, you can see there is a bit of duplication right along the "seam", whereas on right it is a smooth transition.

Closeup of Seam Closeup of No Seam

So, that is nicer, but what else can we do? There is a technique called "Histogram Equalization", which improves contrast.


import cv2
from matplotlib import pyplot as plt
import numpy as np

img_path = "images/ProvinceBrothersStitched.jpg"
img      = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

equ      = cv2.equalizeHist(img)

plt.subplot(1,2,1).set_title("Not Equalized")
plt.imshow(img, cmap='gray',  interpolation='nearest')
plt.subplot(1,2,2).set_title("Histogram Equalized")
plt.imshow(equ, cmap='gray',  interpolation='nearest')
plt.show()

cv2.imwrite("images/ProvinceBrothersEqualized.jpg", equ)

Click the button to see how histogram equalization improves the contrast:

Histogram Equalized
Province Brothers, Equalized

I also played around with denoising, gamma transform, and a number of other options. For this particular image, none of them were a good idea, but they would be in other cases. Denoising will get rid of some of the small specks, but at the cost of making the outlines of small features fuzzier. Gamma transform can make a big improvement if you have not already done histogram equalization, but for this picture at least it doesn't improve the equalized picture. In fact, you could probably end up spending about as much time as you wanted trying out every trick in the OpenCV library; it could become a real time-sink. But, if you're in a hurry, really you can get a lot done in just a very few lines of code (and most of what I have above is for displaying, not the actual processing). I think I may take a look through the other old pictures in my electronic treasure hoard and see what we can do with it.