Files changed (1) hide show
  1. app.py +41 -72
app.py CHANGED
@@ -1,86 +1,43 @@
1
- import gradio as gr
2
- import fitz # PyMuPDF
3
- import cv2
4
- from pdf2image import convert_from_path
5
- import pytesseract
6
- import numpy as np
7
- import os
8
- from fpdf import FPDF
9
-
10
- # Convert PDFs to images
11
- def convert_pdf_to_images(pdf_path, dpi=300):
12
- images = convert_from_path(pdf_path, dpi=dpi, poppler_path="/usr/bin")
13
- return [cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) for image in images]
14
-
15
- # Align images
16
- def align_images(img1, img2):
17
- gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
18
- gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
19
- orb = cv2.ORB_create()
20
- kp1, des1 = orb.detectAndCompute(gray1, None)
21
- kp2, des2 = orb.detectAndCompute(gray2, None)
22
- bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
23
- matches = bf.match(des1, des2)
24
- matches = sorted(matches, key=lambda x: x.distance)
25
- src_pts = np.float32([kp1[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2)
26
- dst_pts = np.float32([kp2[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2)
27
- matrix, _ = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
28
-
29
- # Validate if alignment is good enough
30
- if matrix is None or len(matches) < 10: # Check if sufficient matches exist
31
- raise ValueError("Alignment failed. Insufficient matches between images.")
32
-
33
- aligned_img = cv2.warpPerspective(img2, matrix, (img1.shape[1], img1.shape[0]))
34
- return aligned_img
35
-
36
- # Compare images with noise reduction and filtering
37
- def compare_images(img1, img2):
38
- diff = cv2.absdiff(img1, img2)
39
- gray_diff = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)
40
-
41
- # Apply Gaussian blur to reduce noise
42
- blurred_diff = cv2.GaussianBlur(gray_diff, (5, 5), 0)
43
-
44
- # Apply thresholding
45
- _, thresh = cv2.threshold(blurred_diff, 40, 255, cv2.THRESH_BINARY)
46
-
47
- # Morphological operations to smooth out noise
48
- kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
49
- cleaned = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
50
-
51
- # Filter out small regions
52
- contours, _ = cv2.findContours(cleaned, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
53
- filtered_mask = np.zeros_like(cleaned)
54
- for cnt in contours:
55
- if cv2.contourArea(cnt) > 100: # Ignore small differences (area < 100 pixels)
56
- cv2.drawContours(filtered_mask, [cnt], -1, 255, -1)
57
-
58
- return filtered_mask
59
-
60
- # Highlight changes
61
  def highlight_changes(img, mask):
62
  overlay = img.copy()
63
  contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
 
64
  for cnt in contours:
65
- if cv2.contourArea(cnt) > 100: # Filter based on area to reduce false positives
66
- x, y, w, h = cv2.boundingRect(cnt)
67
- cv2.rectangle(overlay, (x, y), (x + w, y + h), (0, 0, 255), 2) # Red for changes
68
- return overlay
69
-
70
- # Generate comparison PDF
 
 
 
 
 
71
  def generate_comparison_pdf(original_pdf, edited_pdf):
72
  original_images = convert_pdf_to_images(original_pdf)
73
  edited_images = convert_pdf_to_images(edited_pdf)
74
  combined_images = []
75
- for orig_img, edit_img in zip(original_images, edited_images):
 
 
76
  aligned_img = align_images(orig_img, edit_img)
77
  diff_mask = compare_images(orig_img, aligned_img)
78
- highlighted_img = highlight_changes(edit_img, diff_mask)
 
 
 
 
 
 
79
  # Ensure dimensions match
80
  height = min(orig_img.shape[0], highlighted_img.shape[0])
81
  orig_img_resized = orig_img[:height]
82
  highlighted_img_resized = highlighted_img[:height]
83
  combined_images.append(np.hstack((orig_img_resized, highlighted_img_resized)))
 
 
84
  output_path = "outputs/comparison_result.pdf"
85
  pdf = FPDF()
86
  for img in combined_images:
@@ -89,8 +46,17 @@ def generate_comparison_pdf(original_pdf, edited_pdf):
89
  pdf.add_page()
90
  pdf.image(temp_path, x=10, y=10, w=190)
91
  os.remove(temp_path)
 
 
 
 
 
 
 
 
 
92
  pdf.output(output_path)
93
- return output_path
94
 
95
  # Gradio interface function
96
  def pdf_comparison(original_pdf, edited_pdf):
@@ -103,8 +69,8 @@ def pdf_comparison(original_pdf, edited_pdf):
103
  return "Error: File size exceeds 50 MB. Please upload smaller files."
104
 
105
  # Proceed with PDF comparison
106
- result_path = generate_comparison_pdf(original_pdf.name, edited_pdf.name)
107
- return result_path
108
 
109
  # Gradio interface
110
  interface = gr.Interface(
@@ -113,7 +79,10 @@ interface = gr.Interface(
113
  gr.File(label="Upload Original PDF", file_types=[".pdf"]),
114
  gr.File(label="Upload Edited PDF", file_types=[".pdf"])
115
  ],
116
- outputs=gr.File(label="Download Comparison Report"),
 
 
 
117
  )
118
 
119
  if __name__ == "__main__":
 
1
+ # Highlight changes and categorize small and large differences
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  def highlight_changes(img, mask):
3
  overlay = img.copy()
4
  contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
5
+ summary = []
6
  for cnt in contours:
7
+ area = cv2.contourArea(cnt)
8
+ x, y, w, h = cv2.boundingRect(cnt)
9
+ if area > 500: # Major differences
10
+ cv2.rectangle(overlay, (x, y), (x + w, y + h), (0, 0, 255), 2) # Red
11
+ summary.append(f"Major Difference: Location=({x}, {y}), Size=({w}x{h}), Area={area}")
12
+ elif 100 < area <= 500: # Small differences
13
+ cv2.rectangle(overlay, (x, y), (x + w, y + h), (255, 0, 0), 2) # Blue
14
+ summary.append(f"Small Difference: Location=({x}, {y}), Size=({w}x{h}), Area={area}")
15
+ return overlay, summary
16
+
17
+ # Generate comparison PDF with detailed summary
18
  def generate_comparison_pdf(original_pdf, edited_pdf):
19
  original_images = convert_pdf_to_images(original_pdf)
20
  edited_images = convert_pdf_to_images(edited_pdf)
21
  combined_images = []
22
+ all_summaries = []
23
+
24
+ for page_num, (orig_img, edit_img) in enumerate(zip(original_images, edited_images), start=1):
25
  aligned_img = align_images(orig_img, edit_img)
26
  diff_mask = compare_images(orig_img, aligned_img)
27
+ highlighted_img, summary = highlight_changes(edit_img, diff_mask)
28
+
29
+ # Add page number to summary
30
+ page_summary = [f"Page {page_num}:"]
31
+ page_summary.extend(summary)
32
+ all_summaries.extend(page_summary)
33
+
34
  # Ensure dimensions match
35
  height = min(orig_img.shape[0], highlighted_img.shape[0])
36
  orig_img_resized = orig_img[:height]
37
  highlighted_img_resized = highlighted_img[:height]
38
  combined_images.append(np.hstack((orig_img_resized, highlighted_img_resized)))
39
+
40
+ # Generate the PDF
41
  output_path = "outputs/comparison_result.pdf"
42
  pdf = FPDF()
43
  for img in combined_images:
 
46
  pdf.add_page()
47
  pdf.image(temp_path, x=10, y=10, w=190)
48
  os.remove(temp_path)
49
+
50
+ # Add detailed summary to the PDF
51
+ summary_path = "outputs/summary.txt"
52
+ with open(summary_path, "w") as f:
53
+ f.write("\n".join(all_summaries))
54
+ pdf.add_page()
55
+ pdf.set_font("Arial", size=12)
56
+ pdf.multi_cell(0, 10, "\n".join(all_summaries))
57
+
58
  pdf.output(output_path)
59
+ return output_path, summary_path
60
 
61
  # Gradio interface function
62
  def pdf_comparison(original_pdf, edited_pdf):
 
69
  return "Error: File size exceeds 50 MB. Please upload smaller files."
70
 
71
  # Proceed with PDF comparison
72
+ result_path, summary_path = generate_comparison_pdf(original_pdf.name, edited_pdf.name)
73
+ return result_path, summary_path
74
 
75
  # Gradio interface
76
  interface = gr.Interface(
 
79
  gr.File(label="Upload Original PDF", file_types=[".pdf"]),
80
  gr.File(label="Upload Edited PDF", file_types=[".pdf"])
81
  ],
82
+ outputs=[
83
+ gr.File(label="Download Comparison Report"),
84
+ gr.File(label="Download Detailed Summary")
85
+ ],
86
  )
87
 
88
  if __name__ == "__main__":