Drop the Zoom +/− toolbar actions; show the window through the page's uncovered corners

The wheel (and Fit, Ctrl+0) remain. The stitched page has white in
areas that were never scanned; when the view rotates it to level the
text, those triangles stuck out white against the dark window. The
page is now shown with its exact-white pixels transparent, so the
window colour shows through there instead.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
erik
2026-10-11 21:49:02 +00:00
co-authored by Claude Opus 4.7
parent 0dd807a4e7
commit e9ab616161
+15 -9
View File
@@ -30,12 +30,20 @@ from .ocr import Ocr, layout, paragraph_joins, reflow, text_direction
IMAGE_FILTER = "Images (*.png *.jpg *.jpeg *.tif *.tiff *.bmp *.webp)" IMAGE_FILTER = "Images (*.png *.jpg *.jpeg *.tif *.tiff *.bmp *.webp)"
def to_qimage(img): def to_qimage(img, page=False):
"""BGR or grayscale uint8 array -> QImage (a copy).""" """BGR or grayscale uint8 array -> QImage (a copy). With `page`, exact-white pixels
(the uncovered corners of a stitched page) become transparent, so when the view
rotates the page to level it those corners show the window background through."""
img = np.ascontiguousarray(img) img = np.ascontiguousarray(img)
h, w = img.shape[:2] h, w = img.shape[:2]
fmt = QImage.Format_Grayscale8 if img.ndim == 2 else QImage.Format_BGR888 if not page:
return QImage(img.data, w, h, img.strides[0], fmt).copy() fmt = QImage.Format_Grayscale8 if img.ndim == 2 else QImage.Format_BGR888
return QImage(img.data, w, h, img.strides[0], fmt).copy()
bgra = cv2.cvtColor(img, cv2.COLOR_GRAY2BGRA if img.ndim == 2 else cv2.COLOR_BGR2BGRA)
uncovered = (img == 255).all(axis=-1) if img.ndim == 3 else (img == 255)
bgra[uncovered, 3] = 0
bgra = np.ascontiguousarray(bgra)
return QImage(bgra.data, w, h, bgra.strides[0], QImage.Format_ARGB32).copy()
def imread(path): def imread(path):
@@ -90,7 +98,7 @@ class Bridge(QObject):
self.start_event, self.stop_event = threading.Event(), threading.Event() self.start_event, self.stop_event = threading.Event(), threading.Event()
def publish(self, name, bgr): def publish(self, name, bgr):
(self.frame if name == "frame" else self.page).emit(to_qimage(bgr)) (self.frame if name == "frame" else self.page).emit(to_qimage(bgr, page=(name == "page")))
def set_status(self, **kw): def set_status(self, **kw):
self.state = {**self.state, **kw} self.state = {**self.state, **kw}
@@ -616,8 +624,6 @@ class MainWindow(QMainWindow):
self.act_boxes = act("Boxes", lambda on: self.view.show_boxes(on), "Ctrl+B", "Show recognised lines on the page", True) self.act_boxes = act("Boxes", lambda on: self.view.show_boxes(on), "Ctrl+B", "Show recognised lines on the page", True)
self.act_boxes.setChecked(True) self.act_boxes.setChecked(True)
act("Fit", lambda: self.view.fit(), "Ctrl+0", "Fit the page to the window") act("Fit", lambda: self.view.fit(), "Ctrl+0", "Fit the page to the window")
act("Zoom +", lambda: self.view.zoom(1.25), QKeySequence.ZoomIn)
act("Zoom −", lambda: self.view.zoom(0.8), QKeySequence.ZoomOut)
for a in (self.act_save, self.act_copy): for a in (self.act_save, self.act_copy):
a.setEnabled(False) a.setEnabled(False)
@@ -735,7 +741,7 @@ class MainWindow(QMainWindow):
self.page_id += 1 self.page_id += 1
self.view.set_lines([]) self.view.set_lines([])
self.view.angle = 0.0 self.view.angle = 0.0
self.view.set_image(to_qimage(img)) self.view.set_image(to_qimage(img, page=True))
name = os.path.basename(path) if path else "page" name = os.path.basename(path) if path else "page"
self.page_title = f"Page — {name} ({img.shape[1]}×{img.shape[0]})" self.page_title = f"Page — {name} ({img.shape[1]}×{img.shape[0]})"
self.page_head.setText(self.page_title) self.page_head.setText(self.page_title)
@@ -802,7 +808,7 @@ class MainWindow(QMainWindow):
return return
if flipped: # the boxes are on the page turned the right way up if flipped: # the boxes are on the page turned the right way up
self.image = np.ascontiguousarray(self.image[::-1, ::-1]) self.image = np.ascontiguousarray(self.image[::-1, ::-1])
self.view.set_image(to_qimage(self.image)) self.view.set_image(to_qimage(self.image, page=True))
self.lines = lines self.lines = lines
self.text, spans = layout(lines) self.text, spans = layout(lines)
self.view.set_lines(lines, self.text, spans) self.view.set_lines(lines, self.text, spans)