avans06 commited on
Commit
c23cef2
·
1 Parent(s): 55ee165

Add option to toggle transparency processing

Browse files

Introduced a `use_transparency` parameter to allow users to choose between RGBA (transparent) and RGB (opaque) processing modes via the UI.

Files changed (2) hide show
  1. app.py +130 -59
  2. webui.bat +106 -17
app.py CHANGED
@@ -1489,6 +1489,7 @@ def stitch_pairwise_images(img_composite, img_new,
1489
 
1490
  # --- Function for N-Image Stitching (Primarily for Image List Input) ---
1491
  def stitch_multiple_images(images, # List of NumPy images (BGR/BGRA, potentially pre-cropped)
 
1492
  stitcher_mode_str="SCANS",
1493
  registration_resol=0.6,
1494
  seam_estimation_resol=0.1,
@@ -1509,14 +1510,15 @@ def stitch_multiple_images(images, # List of NumPy images (BGR/BGRA, potentially
1509
  max_blending_height=10000,
1510
  blend_smooth_ksize=15,
1511
  num_blend_levels=4,
1512
- fixed_base_image_index=-1
1513
  ):
1514
  """
1515
  Stitches a list of images. Tries cv2.Stitcher first (unless 'DIRECT_PAIRWISE'),
1516
  otherwise falls back to manual pairwise stitching.
1517
  Returns ONE stitched image (RGB/RGBA) and log.
1518
- Input images should be in BGR format (already potentially cropped by caller).
1519
  Output is RGBA. The 'enable_cropping' param here refers to final black border cropping.
 
1520
  """
1521
  log = log_and_print(f"--- Starting Stitching Process for {len(images)} Provided Images ---\n", "")
1522
  total_start_time = time.time()
@@ -1796,7 +1798,8 @@ def stitch_multiple_images(images, # List of NumPy images (BGR/BGRA, potentially
1796
 
1797
  # Clean up the input image list now that it's processed
1798
  del images
1799
- if 'valid_images' in locals(): del valid_images # Should be same as images now
 
1800
  gc.collect()
1801
 
1802
  # 3. Final Result Check and Return (Handling Alpha Channel)
@@ -1806,15 +1809,26 @@ def stitch_multiple_images(images, # List of NumPy images (BGR/BGRA, potentially
1806
  if stitched_img_bgra is not None and stitched_img_bgra.size > 0:
1807
  log = log_and_print("Stitching process finished for image list.", log)
1808
  try:
1809
- # Check channel count to preserve transparency
1810
- if stitched_img_bgra.shape[2] == 4:
 
 
1811
  # Convert BGRA to RGBA
1812
- stitched_img_rgba = cv2.cvtColor(stitched_img_bgra, cv2.COLOR_BGRA2RGBA)
1813
- log = log_and_print("Converted BGRA to RGBA (Transparency preserved).", log)
 
 
 
 
1814
  else:
1815
- # Convert BGR to RGB
1816
- stitched_img_rgba = cv2.cvtColor(stitched_img_bgra, cv2.COLOR_BGR2RGB)
1817
- log = log_and_print("Converted BGR to RGB.", log)
 
 
 
 
 
1818
 
1819
  del stitched_img_bgra # Release BGRA version
1820
  gc.collect()
@@ -1834,6 +1848,7 @@ def stitch_multiple_images(images, # List of NumPy images (BGR/BGRA, potentially
1834
 
1835
  # --- Video Frame Stitching ---
1836
  def stitch_video_frames(video_path,
 
1837
  crop_top_percent=0.0,
1838
  crop_bottom_percent=0.0,
1839
  enable_cropping=True, # This is for POST-stitch cropping
@@ -1854,10 +1869,11 @@ def stitch_video_frames(video_path,
1854
  sample_interval_ms=3000,
1855
  max_composite_width=10000,
1856
  max_composite_height=10000,
1857
- progress=None):
 
1858
  """
1859
  Reads a video, samples frames, and stitches them sequentially.
1860
- Returns a list of stitched images (RGBA/RGB) and a log.
1861
  """
1862
  log = log_and_print(f"--- Starting Incremental Video Stitching: {os.path.basename(video_path)} ---\n", "")
1863
  log = log_and_print(f"Params: Interval={sample_interval_ms}ms, Transform={transform_model_str}, ORB={orb_nfeatures}, Ratio={match_ratio_thresh}\n", log)
@@ -1867,7 +1883,7 @@ def stitch_video_frames(video_path,
1867
  log = log_and_print(f"Post-Crop Black Borders: {enable_cropping}, Strict Edges: {strict_no_black_edges}\n", log)
1868
  log = log_and_print(f"Blending: Method={blend_method}, GainComp={enable_gain_compensation}, SmoothKSize={blend_smooth_ksize}, MB Levels={num_blend_levels}\n", log)
1869
  total_start_time = time.time()
1870
- stitched_results_rgba = [] # Store final RGBA images
1871
 
1872
  cap = cv2.VideoCapture(video_path)
1873
  if not cap.isOpened():
@@ -1900,10 +1916,17 @@ def stitch_video_frames(video_path,
1900
  # Helper to save image to results list with correct color conversion
1901
  def append_result(img_bgr_or_bgra):
1902
  try:
1903
- if img_bgr_or_bgra.shape[2] == 4:
1904
- stitched_results_rgba.append(cv2.cvtColor(img_bgr_or_bgra, cv2.COLOR_BGRA2RGBA))
 
 
 
1905
  else:
1906
- stitched_results_rgba.append(cv2.cvtColor(img_bgr_or_bgra, cv2.COLOR_BGR2RGB))
 
 
 
 
1907
  return True
1908
  except Exception as e:
1909
  print(f"Error converting result: {e}")
@@ -1925,25 +1948,41 @@ def stitch_video_frames(video_path,
1925
  if frame_bgr_raw is not None and frame_bgr_raw.size > 0:
1926
  processed_sampled_count += 1
1927
  frame_bgra = None # Initialize BGR frame variable
1928
-
1929
- # Ensure BGRA for processing
1930
- if frame_bgr_raw.ndim == 2:
1931
- frame_bgra = cv2.cvtColor(frame_bgr_raw, cv2.COLOR_GRAY2BGRA)
1932
- elif frame_bgr_raw.ndim == 3 and frame_bgr_raw.shape[2] == 3:
1933
- # Add alpha channel (opaque)
1934
- frame_bgra = cv2.cvtColor(frame_bgr_raw, cv2.COLOR_BGR2BGRA)
1935
- elif frame_bgr_raw.ndim == 3 and frame_bgr_raw.shape[2] == 4:
1936
- frame_bgra = frame_bgr_raw
 
 
 
 
 
 
 
1937
  else:
1938
- log = log_and_print(f"Warning: Skipping frame {frame_num} due to unexpected shape {frame_bgr_raw.shape}\n", log)
1939
- if frame_bgr_raw is not None:
1940
- del frame_bgr_raw # Clean up original frame
1941
- gc.collect()
1942
- frame_num += 1
1943
- continue # Skip to next frame read
 
 
 
 
 
 
 
 
 
1944
 
1945
  # Release the raw frame once converted/checked (if a copy was made)
1946
- if frame_bgra is not frame_bgr_raw:
1947
  del frame_bgr_raw
1948
  frame_bgr_raw = None # Mark as deleted
1949
  gc.collect()
@@ -2246,6 +2285,7 @@ def stitch_video_frames(video_path,
2246
 
2247
  # --- Gradio Interface Function ---
2248
  def run_stitching_interface(input_files,
 
2249
  crop_top_percent,
2250
  crop_bottom_percent,
2251
  stitcher_mode_str, # For cv2.Stitcher
@@ -2413,6 +2453,7 @@ def run_stitching_interface(input_files,
2413
 
2414
  final_stitched_images_rgba, stitch_log = stitch_video_frames(
2415
  video_path,
 
2416
  crop_top_percent=crop_top_percent,
2417
  crop_bottom_percent=crop_bottom_percent,
2418
  enable_cropping=enable_cropping, # Post-stitch crop
@@ -2445,11 +2486,16 @@ def run_stitching_interface(input_files,
2445
  try:
2446
  n = np.fromfile(img_path, np.uint8)
2447
  if n.size > 0:
 
 
2448
  # Use IMREAD_UNCHANGED to keep Alpha
2449
  img = cv2.imdecode(n, cv2.IMREAD_UNCHANGED)
 
 
2450
  else:
2451
- log = log_and_print(f"Error: File is empty: {os.path.basename(img_path)}. Skipping.\n", log)
2452
- continue
 
2453
  if img is None:
2454
  raise ValueError("imdecode returned None")
2455
  except Exception as e_read:
@@ -2458,22 +2504,36 @@ def run_stitching_interface(input_files,
2458
  del img
2459
  continue # Skip to the next image
2460
 
2461
- # Convert to BGRA for consistency
 
2462
  img_bgra = None
2463
  try:
2464
- if img.ndim == 2:
2465
- img_bgra = cv2.cvtColor(img, cv2.COLOR_GRAY2BGRA)
2466
- elif img.ndim == 3 and img.shape[2] == 3:
2467
- img_bgra = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
2468
- elif img.ndim == 3 and img.shape[2] == 4:
2469
- img_bgra = img # Already BGRA
 
 
 
 
 
 
 
 
2470
  else:
2471
- log = log_and_print(f"Error: Invalid image shape {img.shape} for {os.path.basename(img_path)}. Skipping.\n", log)
2472
- del img
2473
- if 'img_bgra' in locals() and img_bgra is not None:
2474
- del img_bgra
2475
- gc.collect()
2476
- continue # Skip to the next image
 
 
 
 
 
2477
  except cv2.error as e_cvt_color:
2478
  log = log_and_print(f"Error converting image color for {os.path.basename(img_path)}: {e_cvt_color}. Skipping.\n", log)
2479
  del img
@@ -2516,7 +2576,8 @@ def run_stitching_interface(input_files,
2516
 
2517
  # Call the modified stitch_multiple_images function
2518
  stitched_single_rgba, stitch_log_img = stitch_multiple_images(
2519
- images_bgr_cropped, # Pass the list of cropped images (BGRA)
 
2520
  stitcher_mode_str=stitcher_mode_str,
2521
  registration_resol=registration_resol,
2522
  seam_estimation_resol=seam_estimation_resol,
@@ -2618,12 +2679,19 @@ def run_stitching_interface(input_files,
2618
  full_path = os.path.join(temp_dir, filename)
2619
  img_bgra = None # Initialize for finally block
2620
  try:
2621
- # Handle RGBA to BGRA for saving
2622
- # The result coming from stitcher is in RGB(A) format
2623
- if img_rgba.shape[2] == 4:
2624
- img_bgra = cv2.cvtColor(img_rgba, cv2.COLOR_RGBA2BGRA)
 
 
 
2625
  else:
2626
- img_bgra = cv2.cvtColor(img_rgba, cv2.COLOR_RGB2BGR)
 
 
 
 
2627
 
2628
  # Use imencode -> write pattern for better handling of paths/special chars
2629
  is_success, buf = cv2.imencode('.png', img_bgra)
@@ -2685,6 +2753,8 @@ with gr.Blocks() as demo:
2685
 
2686
  # --- Parameters Grouping ---
2687
  with gr.Accordion("Preprocessing Settings", open=True):
 
 
2688
  crop_top_percent = gr.Slider(0.0, 49.0, step=0.5, value=0.0, label="Crop Top %",
2689
  info="Percentage of height to remove from the TOP of each image/frame BEFORE stitching.")
2690
  crop_bottom_percent = gr.Slider(0.0, 49.0, step=0.5, value=0.0, label="Crop Bottom %",
@@ -2762,6 +2832,7 @@ with gr.Blocks() as demo:
2762
  inputs=[
2763
  input_files,
2764
  # Preprocessing
 
2765
  crop_top_percent,
2766
  crop_bottom_percent,
2767
  # OpenCV Stitcher (Image List)
@@ -2797,7 +2868,7 @@ with gr.Blocks() as demo:
2797
  examples = [
2798
  [
2799
  ["examples/Wetter-Panorama/Wetter-Panorama1[NIuO6hrFTrg].mp4"],
2800
- 0, 20,
2801
  "DIRECT_PAIRWISE", 0.6, 0.1, -1, False, "GAIN_BLOCKS",
2802
  True, False, -1,
2803
  "Homography", "Multi-Band", True, 5000, 0.5, 5.0, 0.5, 10000, 10000, 15, 4,
@@ -2805,7 +2876,7 @@ with gr.Blocks() as demo:
2805
  ],
2806
  [
2807
  ["examples/Wetter-Panorama/Wetter-Panorama2[NIuO6hrFTrg].mp4"],
2808
- 0, 20,
2809
  "DIRECT_PAIRWISE", 0.6, 0.1, -1, False, "GAIN_BLOCKS",
2810
  True, False, -1,
2811
  "Homography", "Multi-Band", True, 5000, 0.5, 5.0, 0.5, 10000, 10000, 15, 4,
@@ -2814,7 +2885,7 @@ with gr.Blocks() as demo:
2814
  [
2815
  ["examples/NieRAutomata/nier2B_01.jpg", "examples/NieRAutomata/nier2B_02.jpg", "examples/NieRAutomata/nier2B_03.jpg", "examples/NieRAutomata/nier2B_04.jpg", "examples/NieRAutomata/nier2B_05.jpg",
2816
  "examples/NieRAutomata/nier2B_06.jpg", "examples/NieRAutomata/nier2B_07.jpg", "examples/NieRAutomata/nier2B_08.jpg", "examples/NieRAutomata/nier2B_09.jpg", "examples/NieRAutomata/nier2B_10.jpg", ],
2817
- 0, 0,
2818
  "PANORAMA", 0.6, 0.1, -1, False, "GAIN_BLOCKS",
2819
  True, False, -1,
2820
  "Homography", "Multi-Band", True, 5000, 0.5, 5.0, 0.5, 10000, 10000, 15, 4,
@@ -2822,7 +2893,7 @@ with gr.Blocks() as demo:
2822
  ],
2823
  [
2824
  ["examples/cat/cat_left.jpg", "examples/cat/cat_right.jpg"],
2825
- 0, 0,
2826
  "SCANS", 0.6, 0.1, -1, False, "GAIN_BLOCKS",
2827
  True, False, -1,
2828
  "Affine_Partial", "Linear", True, 5000, 0.5, 5.0, 0.5, 10000, 10000, 15, 4,
@@ -2830,7 +2901,7 @@ with gr.Blocks() as demo:
2830
  ],
2831
  [
2832
  ["examples/ギルドの受付嬢ですが/Girumasu_1.jpg", "examples/ギルドの受付嬢ですが/Girumasu_2.jpg", "examples/ギルドの受付嬢ですが/Girumasu_3.jpg"],
2833
- 0, 0,
2834
  "PANORAMA", 0.6, 0.1, -1, False, "GAIN_BLOCKS",
2835
  True, False, -1,
2836
  "Affine_Partial", "Linear", True, 5000, 0.65, 5.0, 0.5, 10000, 10000, 15, 4,
@@ -2838,7 +2909,7 @@ with gr.Blocks() as demo:
2838
  ],
2839
  [
2840
  ["examples/photographs1/img1.jpg", "examples/photographs1/img2.jpg", "examples/photographs1/img3.jpg", "examples/photographs1/img4.jpg"],
2841
- 0, 0,
2842
  "PANORAMA", 0.6, 0.1, -1, True, "GAIN_BLOCKS",
2843
  True, False, -1,
2844
  "Homography", "Linear", True, 5000, 0.5, 5.0, 0.5, 10000, 10000, 15, 4,
 
1489
 
1490
  # --- Function for N-Image Stitching (Primarily for Image List Input) ---
1491
  def stitch_multiple_images(images, # List of NumPy images (BGR/BGRA, potentially pre-cropped)
1492
+ use_transparency=True,
1493
  stitcher_mode_str="SCANS",
1494
  registration_resol=0.6,
1495
  seam_estimation_resol=0.1,
 
1510
  max_blending_height=10000,
1511
  blend_smooth_ksize=15,
1512
  num_blend_levels=4,
1513
+ fixed_base_image_index=-1,
1514
  ):
1515
  """
1516
  Stitches a list of images. Tries cv2.Stitcher first (unless 'DIRECT_PAIRWISE'),
1517
  otherwise falls back to manual pairwise stitching.
1518
  Returns ONE stitched image (RGB/RGBA) and log.
1519
+ Input images should be in BGR or BGRA format (already potentially cropped by caller).
1520
  Output is RGBA. The 'enable_cropping' param here refers to final black border cropping.
1521
+ If use_transparency is False, the final output will be RGB (opaque background).
1522
  """
1523
  log = log_and_print(f"--- Starting Stitching Process for {len(images)} Provided Images ---\n", "")
1524
  total_start_time = time.time()
 
1798
 
1799
  # Clean up the input image list now that it's processed
1800
  del images
1801
+ if 'valid_images' in locals():
1802
+ del valid_images # Should be same as images now
1803
  gc.collect()
1804
 
1805
  # 3. Final Result Check and Return (Handling Alpha Channel)
 
1809
  if stitched_img_bgra is not None and stitched_img_bgra.size > 0:
1810
  log = log_and_print("Stitching process finished for image list.", log)
1811
  try:
1812
+ # Handle final output based on transparency flag
1813
+ if use_transparency:
1814
+ # Preserve/Ensure RGBA
1815
+ if stitched_img_bgra.shape[2] == 4:
1816
  # Convert BGRA to RGBA
1817
+ stitched_img_rgba = cv2.cvtColor(stitched_img_bgra, cv2.COLOR_BGRA2RGBA)
1818
+ log = log_and_print("Converted BGRA to RGBA (Transparency preserved).", log)
1819
+ else:
1820
+ # Source was BGR, make it RGBA (solid alpha)
1821
+ stitched_img_rgba = cv2.cvtColor(stitched_img_bgra, cv2.COLOR_BGR2RGBA)
1822
+ log = log_and_print("Converted BGR to RGBA.", log)
1823
  else:
1824
+ # Force RGB (Drop Alpha / Opaque)
1825
+ if stitched_img_bgra.shape[2] == 4:
1826
+ stitched_img_rgba = cv2.cvtColor(stitched_img_bgra, cv2.COLOR_BGRA2RGB)
1827
+ log = log_and_print("Converted BGRA to RGB (Transparency disabled, alpha dropped).", log)
1828
+ else:
1829
+ # Convert BGR to RGB
1830
+ stitched_img_rgba = cv2.cvtColor(stitched_img_bgra, cv2.COLOR_BGR2RGB)
1831
+ log = log_and_print("Converted BGR to RGB.", log)
1832
 
1833
  del stitched_img_bgra # Release BGRA version
1834
  gc.collect()
 
1848
 
1849
  # --- Video Frame Stitching ---
1850
  def stitch_video_frames(video_path,
1851
+ use_transparency=True,
1852
  crop_top_percent=0.0,
1853
  crop_bottom_percent=0.0,
1854
  enable_cropping=True, # This is for POST-stitch cropping
 
1869
  sample_interval_ms=3000,
1870
  max_composite_width=10000,
1871
  max_composite_height=10000,
1872
+ progress=None,
1873
+ ):
1874
  """
1875
  Reads a video, samples frames, and stitches them sequentially.
1876
+ Returns a list of stitched images (RGBA if transparency=True, else RGB) and a log.
1877
  """
1878
  log = log_and_print(f"--- Starting Incremental Video Stitching: {os.path.basename(video_path)} ---\n", "")
1879
  log = log_and_print(f"Params: Interval={sample_interval_ms}ms, Transform={transform_model_str}, ORB={orb_nfeatures}, Ratio={match_ratio_thresh}\n", log)
 
1883
  log = log_and_print(f"Post-Crop Black Borders: {enable_cropping}, Strict Edges: {strict_no_black_edges}\n", log)
1884
  log = log_and_print(f"Blending: Method={blend_method}, GainComp={enable_gain_compensation}, SmoothKSize={blend_smooth_ksize}, MB Levels={num_blend_levels}\n", log)
1885
  total_start_time = time.time()
1886
+ stitched_results_rgba = [] # Store final images (RGBA or RGB)
1887
 
1888
  cap = cv2.VideoCapture(video_path)
1889
  if not cap.isOpened():
 
1916
  # Helper to save image to results list with correct color conversion
1917
  def append_result(img_bgr_or_bgra):
1918
  try:
1919
+ if use_transparency:
1920
+ if img_bgr_or_bgra.shape[2] == 4:
1921
+ stitched_results_rgba.append(cv2.cvtColor(img_bgr_or_bgra, cv2.COLOR_BGRA2RGBA))
1922
+ else:
1923
+ stitched_results_rgba.append(cv2.cvtColor(img_bgr_or_bgra, cv2.COLOR_BGR2RGBA))
1924
  else:
1925
+ # Force RGB Output
1926
+ if img_bgr_or_bgra.shape[2] == 4:
1927
+ stitched_results_rgba.append(cv2.cvtColor(img_bgr_or_bgra, cv2.COLOR_BGRA2RGB))
1928
+ else:
1929
+ stitched_results_rgba.append(cv2.cvtColor(img_bgr_or_bgra, cv2.COLOR_BGR2RGB))
1930
  return True
1931
  except Exception as e:
1932
  print(f"Error converting result: {e}")
 
1948
  if frame_bgr_raw is not None and frame_bgr_raw.size > 0:
1949
  processed_sampled_count += 1
1950
  frame_bgra = None # Initialize BGR frame variable
1951
+
1952
+ # Handle Transparency Input
1953
+ if not use_transparency:
1954
+ # If transparency is disabled, ensure we treat the input as Opaque BGRA
1955
+ # We must use BGRA for the internal stitcher to work (warping masks),
1956
+ # but we ensure the Alpha channel is full (255).
1957
+ if frame_bgr_raw.ndim == 3 and frame_bgr_raw.shape[2] == 3:
1958
+ frame_bgra = cv2.cvtColor(frame_bgr_raw, cv2.COLOR_BGR2BGRA)
1959
+ elif frame_bgr_raw.ndim == 3 and frame_bgr_raw.shape[2] == 4:
1960
+ # Drop existing alpha and replace with opaque
1961
+ temp_bgr = cv2.cvtColor(frame_bgr_raw, cv2.COLOR_BGRA2BGR)
1962
+ frame_bgra = cv2.cvtColor(temp_bgr, cv2.COLOR_BGR2BGRA)
1963
+ del temp_bgr
1964
+ else:
1965
+ # Grayscale
1966
+ frame_bgra = cv2.cvtColor(frame_bgr_raw, cv2.COLOR_GRAY2BGRA)
1967
  else:
1968
+ # Standard Transparency Handling
1969
+ if frame_bgr_raw.ndim == 2:
1970
+ frame_bgra = cv2.cvtColor(frame_bgr_raw, cv2.COLOR_GRAY2BGRA)
1971
+ elif frame_bgr_raw.ndim == 3 and frame_bgr_raw.shape[2] == 3:
1972
+ # Add alpha channel (opaque)
1973
+ frame_bgra = cv2.cvtColor(frame_bgr_raw, cv2.COLOR_BGR2BGRA)
1974
+ elif frame_bgr_raw.ndim == 3 and frame_bgr_raw.shape[2] == 4:
1975
+ frame_bgra = frame_bgr_raw
1976
+ else:
1977
+ log = log_and_print(f"Warning: Skipping frame {frame_num} due to unexpected shape {frame_bgr_raw.shape}\n", log)
1978
+ if frame_bgr_raw is not None:
1979
+ del frame_bgr_raw # Clean up original frame
1980
+ gc.collect()
1981
+ frame_num += 1
1982
+ continue # Skip to next frame read
1983
 
1984
  # Release the raw frame once converted/checked (if a copy was made)
1985
+ if frame_bgra is not frame_bgr_raw and frame_bgr_raw is not None:
1986
  del frame_bgr_raw
1987
  frame_bgr_raw = None # Mark as deleted
1988
  gc.collect()
 
2285
 
2286
  # --- Gradio Interface Function ---
2287
  def run_stitching_interface(input_files,
2288
+ use_transparency,
2289
  crop_top_percent,
2290
  crop_bottom_percent,
2291
  stitcher_mode_str, # For cv2.Stitcher
 
2453
 
2454
  final_stitched_images_rgba, stitch_log = stitch_video_frames(
2455
  video_path,
2456
+ use_transparency=use_transparency,
2457
  crop_top_percent=crop_top_percent,
2458
  crop_bottom_percent=crop_bottom_percent,
2459
  enable_cropping=enable_cropping, # Post-stitch crop
 
2486
  try:
2487
  n = np.fromfile(img_path, np.uint8)
2488
  if n.size > 0:
2489
+ # Load logic based on transparency flag
2490
+ if use_transparency:
2491
  # Use IMREAD_UNCHANGED to keep Alpha
2492
  img = cv2.imdecode(n, cv2.IMREAD_UNCHANGED)
2493
+ else:
2494
+ img = cv2.imdecode(n, cv2.IMREAD_COLOR) # Ignore alpha
2495
  else:
2496
+ log = log_and_print(f"Error: File is empty: {os.path.basename(img_path)}. Skipping.\n", log)
2497
+ continue
2498
+
2499
  if img is None:
2500
  raise ValueError("imdecode returned None")
2501
  except Exception as e_read:
 
2504
  del img
2505
  continue # Skip to the next image
2506
 
2507
+ # Convert to BGRA for internal processing consistency
2508
+ # (Internal stitching needs masks, so we use BGRA format even if opaque)
2509
  img_bgra = None
2510
  try:
2511
+ if use_transparency:
2512
+ if img.ndim == 2:
2513
+ img_bgra = cv2.cvtColor(img, cv2.COLOR_GRAY2BGRA)
2514
+ elif img.ndim == 3 and img.shape[2] == 3:
2515
+ img_bgra = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
2516
+ elif img.ndim == 3 and img.shape[2] == 4:
2517
+ img_bgra = img # Already BGRA
2518
+ else:
2519
+ log = log_and_print(f"Error: Invalid image shape {img.shape} for {os.path.basename(img_path)}. Skipping.\n", log)
2520
+ del img
2521
+ if 'img_bgra' in locals() and img_bgra is not None:
2522
+ del img_bgra
2523
+ gc.collect()
2524
+ continue # Skip to the next image
2525
  else:
2526
+ # Force Opaque BGRA
2527
+ if img.ndim == 2:
2528
+ img_bgra = cv2.cvtColor(img, cv2.COLOR_GRAY2BGRA)
2529
+ elif img.ndim == 3 and img.shape[2] == 3:
2530
+ img_bgra = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
2531
+ elif img.ndim == 3 and img.shape[2] == 4:
2532
+ # Drop alpha, re-add opaque
2533
+ tmp = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
2534
+ img_bgra = cv2.cvtColor(tmp, cv2.COLOR_BGR2BGRA)
2535
+ del tmp
2536
+
2537
  except cv2.error as e_cvt_color:
2538
  log = log_and_print(f"Error converting image color for {os.path.basename(img_path)}: {e_cvt_color}. Skipping.\n", log)
2539
  del img
 
2576
 
2577
  # Call the modified stitch_multiple_images function
2578
  stitched_single_rgba, stitch_log_img = stitch_multiple_images(
2579
+ images_bgr_cropped, # Pass the list of cropped images (BGRA/BGR)
2580
+ use_transparency=use_transparency,
2581
  stitcher_mode_str=stitcher_mode_str,
2582
  registration_resol=registration_resol,
2583
  seam_estimation_resol=seam_estimation_resol,
 
2679
  full_path = os.path.join(temp_dir, filename)
2680
  img_bgra = None # Initialize for finally block
2681
  try:
2682
+ if use_transparency:
2683
+ # Handle RGBA to BGRA for saving
2684
+ # The result coming from stitcher is in RGB(A) format
2685
+ if img_rgba.shape[2] == 4:
2686
+ img_bgra = cv2.cvtColor(img_rgba, cv2.COLOR_RGBA2BGRA)
2687
+ else:
2688
+ img_bgra = cv2.cvtColor(img_rgba, cv2.COLOR_RGB2BGR)
2689
  else:
2690
+ # Transparency Disabled: Output should be RGB (saved as BGR)
2691
+ if img_rgba.shape[2] == 4:
2692
+ img_bgra = cv2.cvtColor(img_rgba, cv2.COLOR_RGBA2BGR) # Drop Alpha
2693
+ else:
2694
+ img_bgra = cv2.cvtColor(img_rgba, cv2.COLOR_RGB2BGR)
2695
 
2696
  # Use imencode -> write pattern for better handling of paths/special chars
2697
  is_success, buf = cv2.imencode('.png', img_bgra)
 
2753
 
2754
  # --- Parameters Grouping ---
2755
  with gr.Accordion("Preprocessing Settings", open=True):
2756
+ use_transparency = gr.Checkbox(value=True, label="Use Transparency",
2757
+ info="If Checked: Preserves alpha channel (RGBA). Unchecked: Treats images as opaque and outputs RGB (Black background for warped areas).")
2758
  crop_top_percent = gr.Slider(0.0, 49.0, step=0.5, value=0.0, label="Crop Top %",
2759
  info="Percentage of height to remove from the TOP of each image/frame BEFORE stitching.")
2760
  crop_bottom_percent = gr.Slider(0.0, 49.0, step=0.5, value=0.0, label="Crop Bottom %",
 
2832
  inputs=[
2833
  input_files,
2834
  # Preprocessing
2835
+ use_transparency,
2836
  crop_top_percent,
2837
  crop_bottom_percent,
2838
  # OpenCV Stitcher (Image List)
 
2868
  examples = [
2869
  [
2870
  ["examples/Wetter-Panorama/Wetter-Panorama1[NIuO6hrFTrg].mp4"],
2871
+ True, 0, 20,
2872
  "DIRECT_PAIRWISE", 0.6, 0.1, -1, False, "GAIN_BLOCKS",
2873
  True, False, -1,
2874
  "Homography", "Multi-Band", True, 5000, 0.5, 5.0, 0.5, 10000, 10000, 15, 4,
 
2876
  ],
2877
  [
2878
  ["examples/Wetter-Panorama/Wetter-Panorama2[NIuO6hrFTrg].mp4"],
2879
+ True, 0, 20,
2880
  "DIRECT_PAIRWISE", 0.6, 0.1, -1, False, "GAIN_BLOCKS",
2881
  True, False, -1,
2882
  "Homography", "Multi-Band", True, 5000, 0.5, 5.0, 0.5, 10000, 10000, 15, 4,
 
2885
  [
2886
  ["examples/NieRAutomata/nier2B_01.jpg", "examples/NieRAutomata/nier2B_02.jpg", "examples/NieRAutomata/nier2B_03.jpg", "examples/NieRAutomata/nier2B_04.jpg", "examples/NieRAutomata/nier2B_05.jpg",
2887
  "examples/NieRAutomata/nier2B_06.jpg", "examples/NieRAutomata/nier2B_07.jpg", "examples/NieRAutomata/nier2B_08.jpg", "examples/NieRAutomata/nier2B_09.jpg", "examples/NieRAutomata/nier2B_10.jpg", ],
2888
+ True, 0, 0,
2889
  "PANORAMA", 0.6, 0.1, -1, False, "GAIN_BLOCKS",
2890
  True, False, -1,
2891
  "Homography", "Multi-Band", True, 5000, 0.5, 5.0, 0.5, 10000, 10000, 15, 4,
 
2893
  ],
2894
  [
2895
  ["examples/cat/cat_left.jpg", "examples/cat/cat_right.jpg"],
2896
+ True, 0, 0,
2897
  "SCANS", 0.6, 0.1, -1, False, "GAIN_BLOCKS",
2898
  True, False, -1,
2899
  "Affine_Partial", "Linear", True, 5000, 0.5, 5.0, 0.5, 10000, 10000, 15, 4,
 
2901
  ],
2902
  [
2903
  ["examples/ギルドの受付嬢ですが/Girumasu_1.jpg", "examples/ギルドの受付嬢ですが/Girumasu_2.jpg", "examples/ギルドの受付嬢ですが/Girumasu_3.jpg"],
2904
+ True, 0, 0,
2905
  "PANORAMA", 0.6, 0.1, -1, False, "GAIN_BLOCKS",
2906
  True, False, -1,
2907
  "Affine_Partial", "Linear", True, 5000, 0.65, 5.0, 0.5, 10000, 10000, 15, 4,
 
2909
  ],
2910
  [
2911
  ["examples/photographs1/img1.jpg", "examples/photographs1/img2.jpg", "examples/photographs1/img3.jpg", "examples/photographs1/img4.jpg"],
2912
+ True, 0, 0,
2913
  "PANORAMA", 0.6, 0.1, -1, True, "GAIN_BLOCKS",
2914
  True, False, -1,
2915
  "Homography", "Linear", True, 5000, 0.5, 5.0, 0.5, 10000, 10000, 15, 4,
webui.bat CHANGED
@@ -1,21 +1,37 @@
1
  @echo off
2
 
3
- :: The source of the webui.bat file is stable-diffusion-webui
4
- :: set COMMANDLINE_ARGS=--whisper_implementation faster-whisper --input_audio_max_duration -1 --default_model_name large-v2 --auto_parallel True --output_dir output --vad_max_merge_size 90 --save_downloaded_files --autolaunch
5
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  if not defined PYTHON (set PYTHON=python)
7
- if not defined VENV_DIR (set "VENV_DIR=%~dp0%venv")
 
8
 
9
  mkdir tmp 2>NUL
10
 
 
11
  %PYTHON% -c "" >tmp/stdout.txt 2>tmp/stderr.txt
12
  if %ERRORLEVEL% == 0 goto :check_pip
13
  echo Couldn't launch python
14
  goto :show_stdout_stderr
15
 
16
  :check_pip
 
17
  %PYTHON% -mpip --help >tmp/stdout.txt 2>tmp/stderr.txt
18
  if %ERRORLEVEL% == 0 goto :start_venv
 
19
  if "%PIP_INSTALLER_LOCATION%" == "" goto :show_stdout_stderr
20
  %PYTHON% "%PIP_INSTALLER_LOCATION%" >tmp/stdout.txt 2>tmp/stderr.txt
21
  if %ERRORLEVEL% == 0 goto :start_venv
@@ -23,33 +39,106 @@ echo Couldn't install pip
23
  goto :show_stdout_stderr
24
 
25
  :start_venv
26
- if ["%VENV_DIR%"] == ["-"] goto :skip_venv
27
- if ["%SKIP_VENV%"] == ["1"] goto :skip_venv
 
 
28
 
 
29
  dir "%VENV_DIR%\Scripts\Python.exe" >tmp/stdout.txt 2>tmp/stderr.txt
30
- if %ERRORLEVEL% == 0 goto :activate_venv
31
 
 
 
32
  for /f "delims=" %%i in ('CALL %PYTHON% -c "import sys; print(sys.executable)"') do set PYTHON_FULLNAME="%%i"
33
  echo Creating venv in directory %VENV_DIR% using python %PYTHON_FULLNAME%
34
  %PYTHON_FULLNAME% -m venv "%VENV_DIR%" >tmp/stdout.txt 2>tmp/stderr.txt
35
- if %ERRORLEVEL% == 0 goto :activate_venv
36
- echo Unable to create venv in directory "%VENV_DIR%"
37
- goto :show_stdout_stderr
38
-
39
- :activate_venv
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  set PYTHON="%VENV_DIR%\Scripts\Python.exe"
41
- echo venv %PYTHON%
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
- :skip_venv
 
 
 
 
44
  goto :launch
45
 
46
  :launch
47
- %PYTHON% app.py %COMMANDLINE_ARGS% %*
 
 
 
48
  pause
49
  exit /b
50
 
51
- :show_stdout_stderr
 
 
 
 
 
 
 
 
52
 
 
 
 
53
  echo.
54
  echo exit code: %errorlevel%
55
 
@@ -61,13 +150,13 @@ type tmp\stdout.txt
61
 
62
  :show_stderr
63
  for /f %%i in ("tmp\stderr.txt") do set size=%%~zi
64
- if %size% equ 0 goto :show_stderr
65
  echo.
66
  echo stderr:
67
  type tmp\stderr.txt
68
 
69
  :endofscript
70
-
71
  echo.
72
  echo Launch unsuccessful. Exiting.
73
  pause
 
 
1
  @echo off
2
 
3
+ :: The original source of the webui.bat file is stable-diffusion-webui
4
+ :: Modified and enhanced by Gemini with features for venv management and requirements handling.
5
 
6
+ :: --------- Configuration ---------
7
+ set COMMANDLINE_ARGS=
8
+ :: Define the name of the Launch application
9
+ set APPLICATION_NAME=app.py
10
+ :: Define the name of the virtual environment directory
11
+ set VENV_NAME=venv
12
+ :: Set to 1 to always attempt to update packages from requirements.txt on every launch
13
+ set ALWAYS_UPDATE_REQS=0
14
+ :: ---------------------------------
15
+
16
+
17
+ :: Set PYTHON executable if not already defined
18
  if not defined PYTHON (set PYTHON=python)
19
+ :: Set VENV_DIR using VENV_NAME if not already defined
20
+ if not defined VENV_DIR (set "VENV_DIR=%~dp0%VENV_NAME%")
21
 
22
  mkdir tmp 2>NUL
23
 
24
+ :: Check if Python is callable
25
  %PYTHON% -c "" >tmp/stdout.txt 2>tmp/stderr.txt
26
  if %ERRORLEVEL% == 0 goto :check_pip
27
  echo Couldn't launch python
28
  goto :show_stdout_stderr
29
 
30
  :check_pip
31
+ :: Check if pip is available
32
  %PYTHON% -mpip --help >tmp/stdout.txt 2>tmp/stderr.txt
33
  if %ERRORLEVEL% == 0 goto :start_venv
34
+ :: If pip is not available and PIP_INSTALLER_LOCATION is set, try to install pip
35
  if "%PIP_INSTALLER_LOCATION%" == "" goto :show_stdout_stderr
36
  %PYTHON% "%PIP_INSTALLER_LOCATION%" >tmp/stdout.txt 2>tmp/stderr.txt
37
  if %ERRORLEVEL% == 0 goto :start_venv
 
39
  goto :show_stdout_stderr
40
 
41
  :start_venv
42
+ :: Skip venv creation/activation if VENV_DIR is explicitly set to "-"
43
+ if ["%VENV_DIR%"] == ["-"] goto :skip_venv_entirely
44
+ :: Skip venv creation/activation if SKIP_VENV is set to "1"
45
+ if ["%SKIP_VENV%"] == ["1"] goto :skip_venv_entirely
46
 
47
+ :: Check if the venv already exists by looking for Python.exe in its Scripts directory
48
  dir "%VENV_DIR%\Scripts\Python.exe" >tmp/stdout.txt 2>tmp/stderr.txt
49
+ if %ERRORLEVEL% == 0 goto :activate_venv_and_maybe_update
50
 
51
+ :: Venv does not exist, create it
52
+ echo Virtual environment not found in "%VENV_DIR%". Creating a new one.
53
  for /f "delims=" %%i in ('CALL %PYTHON% -c "import sys; print(sys.executable)"') do set PYTHON_FULLNAME="%%i"
54
  echo Creating venv in directory %VENV_DIR% using python %PYTHON_FULLNAME%
55
  %PYTHON_FULLNAME% -m venv "%VENV_DIR%" >tmp/stdout.txt 2>tmp/stderr.txt
56
+ if %ERRORLEVEL% NEQ 0 (
57
+ echo Unable to create venv in directory "%VENV_DIR%"
58
+ goto :show_stdout_stderr
59
+ )
60
+ echo Venv created.
61
+
62
+ :: Install requirements for the first time if venv was just created
63
+ :: This section handles the initial installation of packages from requirements.txt
64
+ :: immediately after a new virtual environment is created.
65
+ echo Checking for requirements.txt for initial setup in %~dp0
66
+ if exist "%~dp0requirements.txt" (
67
+ echo Found requirements.txt, attempting to install for initial setup...
68
+ call "%VENV_DIR%\Scripts\activate.bat"
69
+ echo Installing packages from requirements.txt ^(initial setup^)...
70
+ "%VENV_DIR%\Scripts\python.exe" -m pip install -r "%~dp0requirements.txt"
71
+ if %ERRORLEVEL% NEQ 0 (
72
+ echo Failed to install requirements during initial setup. Please check the output above.
73
+ pause
74
+ goto :show_stdout_stderr_custom_pip_initial
75
+ )
76
+ echo Initial requirements installed successfully.
77
+ call "%VENV_DIR%\Scripts\deactivate.bat"
78
+ ) else (
79
+ echo No requirements.txt found for initial setup, skipping package installation.
80
+ )
81
+ goto :activate_venv_and_maybe_update
82
+
83
+
84
+ :activate_venv_and_maybe_update
85
+ :: This label is reached if the venv exists or was just created.
86
+ :: Set PYTHON to point to the venv's Python interpreter.
87
  set PYTHON="%VENV_DIR%\Scripts\Python.exe"
88
+ echo Activating venv: %PYTHON%
89
+
90
+ :: Always update requirements if ALWAYS_UPDATE_REQS is 1
91
+ :: This section allows for updating packages from requirements.txt on every launch
92
+ :: if the ALWAYS_UPDATE_REQS variable is set to 1.
93
+ if defined ALWAYS_UPDATE_REQS (
94
+ if "%ALWAYS_UPDATE_REQS%"=="1" (
95
+ echo ALWAYS_UPDATE_REQS is enabled.
96
+ if exist "%~dp0requirements.txt" (
97
+ echo Attempting to update packages from requirements.txt...
98
+ REM No need to call activate.bat here again, PYTHON is already set to the venv's python
99
+ %PYTHON% -m pip install -r "%~dp0requirements.txt"
100
+ if %ERRORLEVEL% NEQ 0 (
101
+ echo Failed to update requirements. Please check the output above.
102
+ pause
103
+ goto :endofscript
104
+ )
105
+ echo Requirements updated successfully.
106
+ ) else (
107
+ echo ALWAYS_UPDATE_REQS is enabled, but no requirements.txt found. Skipping update.
108
+ )
109
+ ) else (
110
+ echo ALWAYS_UPDATE_REQS is not enabled or not set to 1. Skipping routine update.
111
+ )
112
+ )
113
 
114
+ goto :launch
115
+
116
+ :skip_venv_entirely
117
+ :: This label is reached if venv usage is explicitly skipped.
118
+ echo Skipping venv.
119
  goto :launch
120
 
121
  :launch
122
+ :: Launch the main application
123
+ echo Launching Web UI with arguments: %COMMANDLINE_ARGS% %*
124
+ %PYTHON% %APPLICATION_NAME% %COMMANDLINE_ARGS% %*
125
+ echo Launch finished.
126
  pause
127
  exit /b
128
 
129
+ :show_stdout_stderr_custom_pip_initial
130
+ :: Custom error handler for failures during the initial pip install process.
131
+ echo.
132
+ echo exit code ^(pip initial install^): %errorlevel%
133
+ echo Errors during initial pip install. See output above.
134
+ echo.
135
+ echo Launch unsuccessful. Exiting.
136
+ pause
137
+ exit /b
138
 
139
+
140
+ :show_stdout_stderr
141
+ :: General error handler: displays stdout and stderr from the tmp directory.
142
  echo.
143
  echo exit code: %errorlevel%
144
 
 
150
 
151
  :show_stderr
152
  for /f %%i in ("tmp\stderr.txt") do set size=%%~zi
153
+ if %size% equ 0 goto :endofscript
154
  echo.
155
  echo stderr:
156
  type tmp\stderr.txt
157
 
158
  :endofscript
 
159
  echo.
160
  echo Launch unsuccessful. Exiting.
161
  pause
162
+ exit /b