@@ -1370,6 +1370,9 @@ def __init__(self):
13701370 self .process_start_time = None
13711371 self .current_detection_mode = "Barcode" # Default detection mode
13721372
1373+ # Document mode variables
1374+ self .current_normalized_documents = {} # Store normalized documents {page_index: cv_image}
1375+
13731376 # Camera mode variables
13741377 self .camera_results = [] # Store recent camera detection results
13751378 self .camera_history = [] # Store detection history
@@ -1668,6 +1671,7 @@ def create_control_panel(self):
16681671 for mode , config in DETECTION_MODES .items ():
16691672 self .picture_detection_mode_combo .addItem (f"{ mode } - { config ['description' ]} " )
16701673 self .picture_detection_mode_combo .setCurrentIndex (0 ) # Default to Barcode
1674+ self .picture_detection_mode_combo .currentTextChanged .connect (self .on_picture_detection_mode_changed )
16711675 mode_layout .addWidget (self .picture_detection_mode_combo )
16721676 process_layout .addLayout (mode_layout )
16731677
@@ -1715,6 +1719,14 @@ def create_control_panel(self):
17151719 self .copy_button .clicked .connect (self .copy_to_clipboard )
17161720 actions_layout .addWidget (self .copy_button )
17171721
1722+ # Save normalized document button (only for document mode)
1723+ self .save_document_button = QPushButton ("💾 Save Normalized Document" )
1724+ self .save_document_button .setEnabled (False )
1725+ self .save_document_button .setVisible (False ) # Hidden by default (only show in Document mode)
1726+ self .save_document_button .clicked .connect (self .save_normalized_document )
1727+ self .save_document_button .setToolTip ("Save the normalized document image to file" )
1728+ actions_layout .addWidget (self .save_document_button )
1729+
17181730 clear_button = QPushButton ("🗑️ Clear All" )
17191731 clear_button .clicked .connect (self .clear_all )
17201732 actions_layout .addWidget (clear_button )
@@ -1916,6 +1928,24 @@ def on_detection_mode_changed(self, mode_text):
19161928 self .setWindowTitle (f"Dynamsoft Capture Vision - { current_mode } Scanner" )
19171929 self .log_message (f"🔄 Switched to { current_mode } detection mode" )
19181930
1931+ def on_picture_detection_mode_changed (self , mode_text ):
1932+ """Handle detection mode change in picture mode."""
1933+ mode_name = mode_text .split (" - " )[0 ] # Extract mode name from combo text
1934+
1935+ # Show/hide save document button based on mode
1936+ if hasattr (self , 'save_document_button' ):
1937+ if mode_name == "Document" :
1938+ self .save_document_button .setVisible (True )
1939+ # Enable button only if we have a normalized document for current page
1940+ has_normalized = (hasattr (self , 'current_normalized_documents' ) and
1941+ self .current_page_index in self .current_normalized_documents )
1942+ self .save_document_button .setEnabled (has_normalized )
1943+ else :
1944+ self .save_document_button .setVisible (False )
1945+ self .save_document_button .setEnabled (False )
1946+
1947+ self .log_message (f"🔄 Picture mode switched to { mode_name } detection" )
1948+
19191949 def on_tab_changed (self , index ):
19201950 """Handle tab change events."""
19211951 if index == 0 : # Picture mode
@@ -2616,6 +2646,10 @@ def display_document_results(self, original_image, document_items):
26162646 try :
26172647 if not document_items :
26182648 self .image_widget .set_image (original_image , [])
2649+ # Clear stored normalized document
2650+ if self .current_page_index in self .current_normalized_documents :
2651+ del self .current_normalized_documents [self .current_page_index ]
2652+ self .save_document_button .setEnabled (False )
26192653 return
26202654
26212655 # Create annotated original image
@@ -2649,6 +2683,10 @@ def display_document_results(self, original_image, document_items):
26492683 if normalized_image_data :
26502684 normalized_image = convertImageData2Mat (normalized_image_data )
26512685
2686+ # Store normalized document for saving
2687+ self .current_normalized_documents [self .current_page_index ] = normalized_image
2688+ self .save_document_button .setEnabled (True )
2689+
26522690 # Create side-by-side display
26532691 combined_image = self .create_side_by_side_display (annotated_original , normalized_image )
26542692 self .image_widget .set_image (combined_image , []) # No additional annotations needed
@@ -2658,6 +2696,10 @@ def display_document_results(self, original_image, document_items):
26582696
26592697 # Fallback: just show original with boundaries if normalized image extraction fails
26602698 self .image_widget .set_image (annotated_original , [])
2699+ # Clear stored normalized document and disable save button
2700+ if self .current_page_index in self .current_normalized_documents :
2701+ del self .current_normalized_documents [self .current_page_index ]
2702+ self .save_document_button .setEnabled (False )
26612703
26622704 except Exception as e :
26632705 print (f"Error in document display: { e } " )
@@ -2715,6 +2757,47 @@ def create_side_by_side_display(self, original_image, normalized_image):
27152757 print (f"Error creating side-by-side display: { e } " )
27162758 return original_image
27172759
2760+ def save_normalized_document (self ):
2761+ """Save the current normalized document image to file."""
2762+ if self .current_page_index not in self .current_normalized_documents :
2763+ QMessageBox .warning (self , "No Document" , "No normalized document available to save." )
2764+ return
2765+
2766+ try :
2767+ normalized_image = self .current_normalized_documents [self .current_page_index ]
2768+
2769+ # Generate default filename
2770+ if self .current_file_path :
2771+ base_name = os .path .splitext (os .path .basename (self .current_file_path ))[0 ]
2772+ if len (self .current_pages ) > 1 :
2773+ default_name = f"{ base_name } _page{ self .current_page_index + 1 } _normalized.jpg"
2774+ else :
2775+ default_name = f"{ base_name } _normalized.jpg"
2776+ else :
2777+ default_name = f"normalized_document_page{ self .current_page_index + 1 } .jpg"
2778+
2779+ # Show save dialog
2780+ file_path , _ = QFileDialog .getSaveFileName (
2781+ self ,
2782+ "Save Normalized Document" ,
2783+ default_name ,
2784+ "JPEG files (*.jpg);;PNG files (*.png);;BMP files (*.bmp);;TIFF files (*.tiff);;All files (*.*)"
2785+ )
2786+
2787+ if file_path :
2788+ # Save the image
2789+ success = cv2 .imwrite (file_path , normalized_image )
2790+ if success :
2791+ self .log_message (f"💾 Normalized document saved: { os .path .basename (file_path )} " )
2792+ QMessageBox .information (self , "Save Complete" ,
2793+ f"Normalized document saved successfully to:\n { file_path } " )
2794+ else :
2795+ raise Exception ("Failed to write image file" )
2796+
2797+ except Exception as e :
2798+ self .log_message (f"❌ Save error: { e } " )
2799+ QMessageBox .critical (self , "Save Error" , f"Failed to save normalized document: { e } " )
2800+
27182801 def display_page_results (self ):
27192802 """Display detection results for the current page."""
27202803 self .results_text .clear ()
@@ -3170,6 +3253,10 @@ def clear_all(self):
31703253 self .page_results = {}
31713254 self .current_page_index = 0
31723255
3256+ # Clear normalized documents
3257+ if hasattr (self , 'current_normalized_documents' ):
3258+ self .current_normalized_documents .clear ()
3259+
31733260 # Clear custom receiver
31743261 if self .custom_receiver :
31753262 self .custom_receiver .images .clear ()
@@ -3187,6 +3274,8 @@ def clear_all(self):
31873274 self .process_button .setEnabled (False )
31883275 self .export_button .setEnabled (False )
31893276 self .copy_button .setEnabled (False )
3277+ if hasattr (self , 'save_document_button' ):
3278+ self .save_document_button .setEnabled (False )
31903279
31913280 # Hide navigation
31923281 self .nav_group .hide ()
0 commit comments