@@ -1370,6 +1370,9 @@ def __init__(self):
1370
1370
self .process_start_time = None
1371
1371
self .current_detection_mode = "Barcode" # Default detection mode
1372
1372
1373
+ # Document mode variables
1374
+ self .current_normalized_documents = {} # Store normalized documents {page_index: cv_image}
1375
+
1373
1376
# Camera mode variables
1374
1377
self .camera_results = [] # Store recent camera detection results
1375
1378
self .camera_history = [] # Store detection history
@@ -1668,6 +1671,7 @@ def create_control_panel(self):
1668
1671
for mode , config in DETECTION_MODES .items ():
1669
1672
self .picture_detection_mode_combo .addItem (f"{ mode } - { config ['description' ]} " )
1670
1673
self .picture_detection_mode_combo .setCurrentIndex (0 ) # Default to Barcode
1674
+ self .picture_detection_mode_combo .currentTextChanged .connect (self .on_picture_detection_mode_changed )
1671
1675
mode_layout .addWidget (self .picture_detection_mode_combo )
1672
1676
process_layout .addLayout (mode_layout )
1673
1677
@@ -1715,6 +1719,14 @@ def create_control_panel(self):
1715
1719
self .copy_button .clicked .connect (self .copy_to_clipboard )
1716
1720
actions_layout .addWidget (self .copy_button )
1717
1721
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
+
1718
1730
clear_button = QPushButton ("🗑️ Clear All" )
1719
1731
clear_button .clicked .connect (self .clear_all )
1720
1732
actions_layout .addWidget (clear_button )
@@ -1916,6 +1928,24 @@ def on_detection_mode_changed(self, mode_text):
1916
1928
self .setWindowTitle (f"Dynamsoft Capture Vision - { current_mode } Scanner" )
1917
1929
self .log_message (f"🔄 Switched to { current_mode } detection mode" )
1918
1930
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
+
1919
1949
def on_tab_changed (self , index ):
1920
1950
"""Handle tab change events."""
1921
1951
if index == 0 : # Picture mode
@@ -2616,6 +2646,10 @@ def display_document_results(self, original_image, document_items):
2616
2646
try :
2617
2647
if not document_items :
2618
2648
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 )
2619
2653
return
2620
2654
2621
2655
# Create annotated original image
@@ -2649,6 +2683,10 @@ def display_document_results(self, original_image, document_items):
2649
2683
if normalized_image_data :
2650
2684
normalized_image = convertImageData2Mat (normalized_image_data )
2651
2685
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
+
2652
2690
# Create side-by-side display
2653
2691
combined_image = self .create_side_by_side_display (annotated_original , normalized_image )
2654
2692
self .image_widget .set_image (combined_image , []) # No additional annotations needed
@@ -2658,6 +2696,10 @@ def display_document_results(self, original_image, document_items):
2658
2696
2659
2697
# Fallback: just show original with boundaries if normalized image extraction fails
2660
2698
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 )
2661
2703
2662
2704
except Exception as e :
2663
2705
print (f"Error in document display: { e } " )
@@ -2715,6 +2757,47 @@ def create_side_by_side_display(self, original_image, normalized_image):
2715
2757
print (f"Error creating side-by-side display: { e } " )
2716
2758
return original_image
2717
2759
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
+
2718
2801
def display_page_results (self ):
2719
2802
"""Display detection results for the current page."""
2720
2803
self .results_text .clear ()
@@ -3170,6 +3253,10 @@ def clear_all(self):
3170
3253
self .page_results = {}
3171
3254
self .current_page_index = 0
3172
3255
3256
+ # Clear normalized documents
3257
+ if hasattr (self , 'current_normalized_documents' ):
3258
+ self .current_normalized_documents .clear ()
3259
+
3173
3260
# Clear custom receiver
3174
3261
if self .custom_receiver :
3175
3262
self .custom_receiver .images .clear ()
@@ -3187,6 +3274,8 @@ def clear_all(self):
3187
3274
self .process_button .setEnabled (False )
3188
3275
self .export_button .setEnabled (False )
3189
3276
self .copy_button .setEnabled (False )
3277
+ if hasattr (self , 'save_document_button' ):
3278
+ self .save_document_button .setEnabled (False )
3190
3279
3191
3280
# Hide navigation
3192
3281
self .nav_group .hide ()
0 commit comments