These are my images, I want to
file_1.pdf
and file_2.pdf
) into images.This is my code
from pdf2image import convert_from_path
import cv2
import numpy as np
from PIL import Image, ImageOps
from IPython.display import display`
def process_and_display_image(pdf_path, target_size=(800, 600),save_path='processed_image.jpeg'):
images = convert_from_path(pdf_path)
image = images[0]
image = ImageOps.exif_transpose(image)
image.thumbnail(target_size, Image.Resampling.LANCZOS)
image_np = np.array(image)
image_np = cv2.cvtColor(image_np, cv2.COLOR_RGB2GRAY)
image_processed = Image.fromarray(image_np)
display(image_processed)
image_processed.save(save_path, 'JPEG')
print(f"Image saved as {save_path}")
# Display image from PDF
process_and_display_image("file_1.pdf",save_path='file_1.jpeg')
process_and_display_image("file_2.pdf",save_path='file_2.jpeg')
import matplotlib.pyplot as plt
image1 = cv2.imread('file_1.jpeg', cv2.IMREAD_UNCHANGED)
image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)
image2 = cv2.imread('file_2.jpeg', cv2.IMREAD_UNCHANGED)
image2 = cv2.cvtColor(image2, cv2.COLOR_BGR2RGB)
if image1.shape == image2.shape:
difference = cv2.absdiff(image1,image2)
difference = 255 - difference
if image1.shape == image2.shape:
overlay = cv2.addWeighted(image1, 0.5, image2, 0.5, 0)
difference = overlay
plt.imshow(difference)
plt.axis('off')
plt.show()
I'd like to present some simple operations to get exactly the colors you want, without thresholding.
im1
and im2
(grayscale):
# assuming white sheet of paper, ink is the inverse
# positive = added
# negative = deleted
signed_difference = (255 - im2).astype(np.int16) - (255 - im1).astype(np.int16)
(white = added, black = removed. If you need to look at it, imshow("window", signed_difference / (255*2) + 0.5)
)
Starting from a version that contains only "ink" that's in both pictures, I'll add colored "ink" for the additions and deletions. The reshape
and expand_dims
stuff helps numpy do the right thing.
canvas = np.maximum(im1, im2) # same ink in both images
canvas = cv.cvtColor(canvas, cv.COLOR_GRAY2BGR)
# add colored ink: subtract the inverse of those colors to "ink" the page
add_color = np.array([255, 128, 0]).reshape((1, 1, 3)) # additions blue
del_color = np.array([0, 0, 255]).reshape((1, 1, 3)) # deletions red
strength = np.expand_dims(np.abs(signed_difference) / 255, axis=2)
# those are just for knowing *what pixels* are affected at all.
# results are all grayscale, no thresholding.
add_mask = np.expand_dims(signed_difference > 0, axis=2)
del_mask = np.expand_dims(signed_difference < 0, axis=2)
canvas = np.where(add_mask, canvas - (255 - add_color) * strength, canvas)
canvas = np.where(del_mask, canvas - (255 - del_color) * strength, canvas)
canvas = np.clip(canvas, 0, 255).astype(np.uint8)
And that's it.
You'll want to make sure your pictures are aligned well, even sub-pixel accurately. You can use findTransformECC()
for sub-pixel refinement.
The worse the initial alignment, the more blurring (gaussFiltSize
) you'll need. All kinds of "fixed borders" will ruin this process. Make sure to only feed the "art" in, not any framing.
The core of that operation:
H = np.eye(3).astype(np.float32)
(rv, H) = cv.findTransformECC(
templateImage=im1,
inputImage=im2,
warpMatrix=H[:2],
motionType=cv.MOTION_AFFINE,
criteria=(cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 20, -1), # -1 to ignore eps and use all iterations
inputMask=None,
gaussFiltSize=5
)
im2_aligned = cv.warpAffine(im2, H, (im1.shape[1], im1.shape[0]), flags=cv.INTER_CUBIC + cv.WARP_INVERSE_MAP)
And this is what your input looks like, aligned and colored, without borders:
You'll notice that it also colors gray-to-gray differences. Your reference picture does not show that. Your reference picture was probably made by the original CAD program. The CAD program is aware of the geometry and colors it according to flexible styles. I can only work on the image data. Sure, it's possible to exclude "gray" from the coloring but then you'll also see the gray pixels near black pixels (fuzzy lines) not-colored.
Here's a complete gray-to-gray display: