Upload app.py with huggingface_hub
Browse files
app.py
CHANGED
|
@@ -24,18 +24,42 @@ import threading
|
|
| 24 |
import queue
|
| 25 |
from typing import List, Dict, Any
|
| 26 |
|
| 27 |
-
#
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
'
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
# Constants
|
| 41 |
IMAGENET_MEAN = (0.485, 0.456, 0.406)
|
|
@@ -1087,18 +1111,20 @@ def save_to_html(content, source_type, filename=None):
|
|
| 1087 |
# Function to analyze images with a prompt for folder analysis
|
| 1088 |
def analyze_folder_images(folder_path, prompt):
|
| 1089 |
"""Analyze all images in a folder."""
|
| 1090 |
-
# Add
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1091 |
logger.info(f"analyze_folder_images called with path: '{folder_path}'")
|
| 1092 |
|
| 1093 |
-
# Increment operations counter
|
| 1094 |
-
gui_stats['operations_completed'] += 1
|
| 1095 |
-
|
| 1096 |
if not folder_path or folder_path.strip() == "":
|
| 1097 |
error_msg = "No folder path provided. Please enter a valid folder path."
|
| 1098 |
logger.error(error_msg)
|
| 1099 |
-
|
| 1100 |
-
gui_stats['last_error'] = error_msg
|
| 1101 |
-
gui_stats['last_error_time'] = datetime.datetime.now().strftime("%H:%M:%S")
|
| 1102 |
return error_msg
|
| 1103 |
|
| 1104 |
# Clean up the folder path
|
|
@@ -1118,12 +1144,10 @@ def analyze_folder_images(folder_path, prompt):
|
|
| 1118 |
if os.path.exists("/data"):
|
| 1119 |
potential_paths.append(os.path.join("/data", folder_path))
|
| 1120 |
|
| 1121 |
-
#
|
| 1122 |
-
|
| 1123 |
-
|
| 1124 |
-
|
| 1125 |
-
except Exception as e:
|
| 1126 |
-
logger.warning(f"Could not list current directory: {str(e)}")
|
| 1127 |
|
| 1128 |
# Try each path
|
| 1129 |
valid_path = None
|
|
@@ -1134,6 +1158,7 @@ def analyze_folder_images(folder_path, prompt):
|
|
| 1134 |
if os.path.isdir(test_path):
|
| 1135 |
valid_path = test_path
|
| 1136 |
logger.info(f"Found valid directory path: '{valid_path}'")
|
|
|
|
| 1137 |
break
|
| 1138 |
else:
|
| 1139 |
logger.debug(f"Path exists but is not a directory: '{test_path}'")
|
|
@@ -1141,18 +1166,18 @@ def analyze_folder_images(folder_path, prompt):
|
|
| 1141 |
if not valid_path:
|
| 1142 |
error_msg = f"Could not find a valid directory at '{folder_path}'. Please provide a complete and valid folder path."
|
| 1143 |
logger.error(error_msg)
|
| 1144 |
-
|
| 1145 |
-
gui_stats['last_error'] = error_msg
|
| 1146 |
-
gui_stats['last_error_time'] = datetime.datetime.now().strftime("%H:%M:%S")
|
| 1147 |
|
| 1148 |
# Try to provide helpful information about available directories
|
| 1149 |
try:
|
| 1150 |
available_dirs = [d for d in os.listdir('.') if os.path.isdir(d)]
|
|
|
|
| 1151 |
if available_dirs:
|
| 1152 |
return f"Error: {error_msg}\n\nAvailable directories in current location: {', '.join(available_dirs)}"
|
| 1153 |
else:
|
| 1154 |
return f"Error: {error_msg}\n\nNo directories found in the current location."
|
| 1155 |
-
except:
|
|
|
|
| 1156 |
return f"Error: {error_msg}"
|
| 1157 |
|
| 1158 |
# Convert to Path object for easier handling
|
|
@@ -1163,58 +1188,81 @@ def analyze_folder_images(folder_path, prompt):
|
|
| 1163 |
image_files = []
|
| 1164 |
for ext in SUPPORTED_EXTENSIONS:
|
| 1165 |
logger.debug(f"Searching for files with extension: {ext}")
|
|
|
|
| 1166 |
# Use glob patterns that are case-insensitive
|
| 1167 |
-
|
| 1168 |
-
|
|
|
|
|
|
|
| 1169 |
|
| 1170 |
logger.info(f"Found {len(image_files)} images in {folder_path}")
|
|
|
|
| 1171 |
|
| 1172 |
if not image_files:
|
| 1173 |
error_msg = f"No supported image files found in '{folder_path}'. Supported formats: {', '.join(SUPPORTED_EXTENSIONS)}"
|
| 1174 |
logger.warning(error_msg)
|
| 1175 |
-
|
| 1176 |
-
gui_stats['last_warning'] = error_msg
|
| 1177 |
-
gui_stats['last_warning_time'] = datetime.datetime.now().strftime("%H:%M:%S")
|
| 1178 |
return error_msg
|
| 1179 |
|
| 1180 |
# Sort the files for consistent output
|
| 1181 |
image_files.sort()
|
| 1182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1183 |
results = []
|
| 1184 |
results.append(f"Found {len(image_files)} images in {folder_path}\n")
|
| 1185 |
|
| 1186 |
-
# Load model once for all images
|
| 1187 |
-
logger.info("Loading model for folder analysis")
|
| 1188 |
-
model, tokenizer = load_model()
|
| 1189 |
-
if model is None or tokenizer is None:
|
| 1190 |
-
error_msg = "Error: Could not load model for image analysis"
|
| 1191 |
-
logger.error(error_msg)
|
| 1192 |
-
gui_stats['errors'] += 1
|
| 1193 |
-
gui_stats['last_error'] = error_msg
|
| 1194 |
-
gui_stats['last_error_time'] = datetime.datetime.now().strftime("%H:%M:%S")
|
| 1195 |
-
return error_msg
|
| 1196 |
-
|
| 1197 |
# Process each image
|
| 1198 |
for i, img_path in enumerate(image_files, 1):
|
| 1199 |
try:
|
| 1200 |
logger.info(f"Processing image {i}/{len(image_files)}: {img_path.name}")
|
|
|
|
| 1201 |
|
| 1202 |
# Check if file is a PDF
|
| 1203 |
is_pdf = img_path.suffix.lower() == '.pdf'
|
| 1204 |
|
| 1205 |
if is_pdf:
|
| 1206 |
logger.info(f"Processing PDF file: {img_path}")
|
|
|
|
| 1207 |
try:
|
| 1208 |
# Process PDF pages separately
|
| 1209 |
logger.debug(f"Converting PDF to images: {img_path}")
|
| 1210 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1211 |
|
| 1212 |
if not pdf_images or len(pdf_images) == 0:
|
| 1213 |
error_msg = f"PDF conversion failed for {img_path.name}: No pages extracted"
|
| 1214 |
logger.error(error_msg)
|
| 1215 |
-
|
| 1216 |
-
gui_stats['last_error'] = error_msg
|
| 1217 |
-
gui_stats['last_error_time'] = datetime.datetime.now().strftime("%H:%M:%S")
|
| 1218 |
results.append(f"---\nImage {i}/{len(image_files)}: {img_path.name}\nError: PDF conversion failed - no pages extracted\n")
|
| 1219 |
continue
|
| 1220 |
|
|
@@ -1225,6 +1273,7 @@ def analyze_folder_images(folder_path, prompt):
|
|
| 1225 |
for page_num, page_img in enumerate(pdf_images, 1):
|
| 1226 |
try:
|
| 1227 |
logger.debug(f"Processing PDF page {page_num}/{len(pdf_images)}")
|
|
|
|
| 1228 |
page_prompt = f"PDF {img_path.name} - Page {page_num}/{len(pdf_images)}: {prompt}"
|
| 1229 |
page_result = process_image_with_text(page_img, page_prompt)
|
| 1230 |
pdf_results.append(f"-- Page {page_num} --\n{page_result}")
|
|
@@ -1232,9 +1281,8 @@ def analyze_folder_images(folder_path, prompt):
|
|
| 1232 |
error_msg = f"Error processing PDF page {page_num}: {str(page_err)}"
|
| 1233 |
logger.error(error_msg)
|
| 1234 |
logger.error(traceback.format_exc())
|
| 1235 |
-
|
| 1236 |
-
|
| 1237 |
-
gui_stats['last_error_time'] = datetime.datetime.now().strftime("%H:%M:%S")
|
| 1238 |
pdf_results.append(f"-- Page {page_num} --\nError: {str(page_err)}")
|
| 1239 |
|
| 1240 |
# Add all PDF results
|
|
@@ -1245,16 +1293,17 @@ def analyze_folder_images(folder_path, prompt):
|
|
| 1245 |
error_msg = f"Error processing PDF {img_path.name}: {str(pdf_err)}"
|
| 1246 |
logger.error(error_msg)
|
| 1247 |
logger.error(traceback.format_exc())
|
| 1248 |
-
|
| 1249 |
-
|
| 1250 |
-
gui_stats['last_error_time'] = datetime.datetime.now().strftime("%H:%M:%S")
|
| 1251 |
results.append(f"---\nImage {i}/{len(image_files)}: {img_path.name}\nError processing PDF: {str(pdf_err)}\n")
|
| 1252 |
else:
|
| 1253 |
# Standard image processing
|
| 1254 |
try:
|
| 1255 |
# Open and process the image
|
|
|
|
| 1256 |
image = Image.open(img_path).convert('RGB')
|
| 1257 |
logger.debug(f"Image loaded: size={image.size}, mode={image.mode}")
|
|
|
|
| 1258 |
|
| 1259 |
# Process image
|
| 1260 |
image_prompt = f"Image {i}/{len(image_files)} - {img_path.name}: {prompt}"
|
|
@@ -1266,24 +1315,24 @@ def analyze_folder_images(folder_path, prompt):
|
|
| 1266 |
|
| 1267 |
# Log success
|
| 1268 |
logger.info(f"Successfully processed image {i}/{len(image_files)}: {img_path.name}")
|
|
|
|
| 1269 |
except Exception as img_err:
|
| 1270 |
error_msg = f"Error opening/processing image {img_path.name}: {str(img_err)}"
|
| 1271 |
logger.error(error_msg)
|
| 1272 |
logger.error(traceback.format_exc())
|
| 1273 |
-
|
| 1274 |
-
|
| 1275 |
-
gui_stats['last_error_time'] = datetime.datetime.now().strftime("%H:%M:%S")
|
| 1276 |
results.append(f"---\nImage {i}/{len(image_files)}: {img_path.name}\nError opening/processing image: {str(img_err)}\n")
|
| 1277 |
|
| 1278 |
except Exception as e:
|
| 1279 |
error_msg = f"Error processing image {img_path.name}: {str(e)}"
|
| 1280 |
logger.error(error_msg)
|
| 1281 |
logger.error(traceback.format_exc())
|
| 1282 |
-
|
| 1283 |
-
|
| 1284 |
-
gui_stats['last_error_time'] = datetime.datetime.now().strftime("%H:%M:%S")
|
| 1285 |
results.append(f"---\nImage {i}/{len(image_files)}: {img_path.name}\nError: {str(e)}\n")
|
| 1286 |
|
|
|
|
| 1287 |
combined_result = "\n".join(results)
|
| 1288 |
logger.info(f"Folder analysis complete, processed {len(image_files)} images")
|
| 1289 |
return combined_result
|
|
@@ -1564,24 +1613,6 @@ def get_latest_log_content():
|
|
| 1564 |
|
| 1565 |
# Main function
|
| 1566 |
def main():
|
| 1567 |
-
# Create log handler for UI
|
| 1568 |
-
gui_log_handler = GUILogHandler(max_entries=500)
|
| 1569 |
-
gui_log_handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(message)s'))
|
| 1570 |
-
gui_log_handler.setLevel(logging.DEBUG)
|
| 1571 |
-
logger.addHandler(gui_log_handler)
|
| 1572 |
-
|
| 1573 |
-
# Log startup information
|
| 1574 |
-
logger.info("="*50)
|
| 1575 |
-
logger.info("InternVL2.5 Image Analyzer starting up")
|
| 1576 |
-
logger.info(f"Log file: {log_file}")
|
| 1577 |
-
logger.info(f"Python version: {sys.version}")
|
| 1578 |
-
logger.info(f"Torch version: {torch.__version__}")
|
| 1579 |
-
logger.info(f"CUDA available: {torch.cuda.is_available()}")
|
| 1580 |
-
if torch.cuda.is_available():
|
| 1581 |
-
logger.info(f"CUDA version: {torch.version.cuda}")
|
| 1582 |
-
logger.info(f"GPU: {torch.cuda.get_device_name(0)}")
|
| 1583 |
-
logger.info("="*50)
|
| 1584 |
-
|
| 1585 |
# Load the model
|
| 1586 |
model, tokenizer = load_model()
|
| 1587 |
|
|
@@ -1607,274 +1638,46 @@ def main():
|
|
| 1607 |
"Summarize what you see in this image in one paragraph."
|
| 1608 |
]
|
| 1609 |
|
| 1610 |
-
#
|
| 1611 |
-
def get_debug_logs(num_lines=50):
|
| 1612 |
-
return gui_log_handler.get_logs(last_n=num_lines)
|
| 1613 |
-
|
| 1614 |
-
# Function to read the full log file
|
| 1615 |
def read_log_file():
|
| 1616 |
try:
|
| 1617 |
-
with open(
|
| 1618 |
return f.read()
|
| 1619 |
except Exception as e:
|
| 1620 |
return f"Error reading log file: {str(e)}"
|
| 1621 |
|
| 1622 |
-
# Function to update logs in real-time
|
| 1623 |
-
def update_logs(history):
|
| 1624 |
-
latest = gui_log_handler.get_latest()
|
| 1625 |
-
if latest:
|
| 1626 |
-
history = history + "\n" + latest if history else latest
|
| 1627 |
-
# Keep only the last 50 lines for performance
|
| 1628 |
-
lines = history.split("\n")
|
| 1629 |
-
if len(lines) > 50:
|
| 1630 |
-
history = "\n".join(lines[-50:])
|
| 1631 |
-
return history
|
| 1632 |
-
|
| 1633 |
-
# Function to clear logs
|
| 1634 |
-
def clear_logs():
|
| 1635 |
-
gui_log_handler.clear()
|
| 1636 |
-
return ""
|
| 1637 |
-
|
| 1638 |
# Create tabs for different modes
|
| 1639 |
with gr.Blocks(title="InternVL2.5 Image Analyzer", theme=gr.themes.Soft()) as demo:
|
| 1640 |
gr.Markdown("# InternVL2.5 Image Analyzer")
|
| 1641 |
gr.Markdown("Analyze images using the InternVL2.5 model. You can upload individual images or analyze all images in a folder.")
|
| 1642 |
|
| 1643 |
-
#
|
| 1644 |
-
with gr.Tab("
|
| 1645 |
gr.Markdown("## View Application Logs")
|
| 1646 |
-
gr.Markdown("This tab displays the complete application logs for debugging purposes.")
|
| 1647 |
|
| 1648 |
with gr.Row():
|
| 1649 |
-
with gr.Column(scale=
|
| 1650 |
-
|
| 1651 |
-
label="
|
| 1652 |
value=read_log_file(),
|
| 1653 |
-
lines=
|
| 1654 |
-
max_lines=
|
| 1655 |
-
autoscroll=False
|
| 1656 |
)
|
| 1657 |
with gr.Column(scale=1):
|
| 1658 |
refresh_logs_btn = gr.Button("Refresh Logs")
|
| 1659 |
-
log_info = gr.Markdown(f"Log file
|
| 1660 |
-
|
| 1661 |
-
# Stats display
|
| 1662 |
-
stats_html = gr.HTML(format_debug_stats_html())
|
| 1663 |
-
|
| 1664 |
-
# Function to handle automatic log refresh
|
| 1665 |
-
def refresh_logs():
|
| 1666 |
-
return read_log_file(), format_debug_stats_html()
|
| 1667 |
-
|
| 1668 |
-
refresh_logs_btn.click(
|
| 1669 |
-
fn=refresh_logs,
|
| 1670 |
-
inputs=[],
|
| 1671 |
-
outputs=[full_logs, stats_html]
|
| 1672 |
-
)
|
| 1673 |
-
|
| 1674 |
-
# Auto-refresh logs every 10 seconds
|
| 1675 |
-
gr.on(
|
| 1676 |
-
triggers=[],
|
| 1677 |
-
fn=refresh_logs,
|
| 1678 |
-
inputs=[],
|
| 1679 |
-
outputs=[full_logs, stats_html],
|
| 1680 |
-
every=10
|
| 1681 |
-
)
|
| 1682 |
-
|
| 1683 |
-
# Options for log files
|
| 1684 |
-
log_files = [f for f in os.listdir(LOGS_DIR) if f.endswith('.log')]
|
| 1685 |
-
log_dropdown = gr.Dropdown(
|
| 1686 |
-
label="Available Log Files",
|
| 1687 |
-
choices=log_files,
|
| 1688 |
-
value="latest_debug.log" if "latest_debug.log" in log_files else None
|
| 1689 |
-
)
|
| 1690 |
-
|
| 1691 |
-
# Function to load a specific log file
|
| 1692 |
-
def load_log_file(filename):
|
| 1693 |
-
if not filename:
|
| 1694 |
-
return "No log file selected"
|
| 1695 |
-
|
| 1696 |
-
try:
|
| 1697 |
-
with open(os.path.join(LOGS_DIR, filename), 'r') as f:
|
| 1698 |
-
return f.read()
|
| 1699 |
-
except Exception as e:
|
| 1700 |
-
return f"Error reading {filename}: {str(e)}"
|
| 1701 |
-
|
| 1702 |
-
log_dropdown.change(
|
| 1703 |
-
fn=load_log_file,
|
| 1704 |
-
inputs=[log_dropdown],
|
| 1705 |
-
outputs=[full_logs]
|
| 1706 |
-
)
|
| 1707 |
-
|
| 1708 |
-
# Download log file button
|
| 1709 |
-
def get_current_log_file(filename):
|
| 1710 |
-
if not filename:
|
| 1711 |
-
return None
|
| 1712 |
-
|
| 1713 |
-
log_path = os.path.join(LOGS_DIR, filename)
|
| 1714 |
-
if os.path.exists(log_path):
|
| 1715 |
-
return log_path
|
| 1716 |
-
return None
|
| 1717 |
-
|
| 1718 |
-
download_log_btn = gr.Button("Download Selected Log")
|
| 1719 |
-
log_file_download = gr.File(label="Log File for Download")
|
| 1720 |
-
|
| 1721 |
-
download_log_btn.click(
|
| 1722 |
-
fn=get_current_log_file,
|
| 1723 |
-
inputs=[log_dropdown],
|
| 1724 |
-
outputs=[log_file_download]
|
| 1725 |
-
)
|
| 1726 |
-
|
| 1727 |
-
# Debug mode toggle and panel
|
| 1728 |
-
with gr.Accordion("Debug Console", open=False) as debug_accordion:
|
| 1729 |
-
with gr.Row():
|
| 1730 |
-
with gr.Column(scale=4):
|
| 1731 |
-
debug_output = gr.Textbox(
|
| 1732 |
-
label="Real-time Debug Logs",
|
| 1733 |
-
value=get_debug_logs(20),
|
| 1734 |
-
lines=8,
|
| 1735 |
-
max_lines=15,
|
| 1736 |
-
autoscroll=True,
|
| 1737 |
-
elem_id="debug_output"
|
| 1738 |
-
)
|
| 1739 |
-
with gr.Column(scale=1):
|
| 1740 |
-
with gr.Row():
|
| 1741 |
-
clear_btn = gr.Button("Clear Logs")
|
| 1742 |
-
refresh_btn = gr.Button("Refresh")
|
| 1743 |
-
|
| 1744 |
-
debug_level = gr.Radio(
|
| 1745 |
-
["ERROR", "WARNING", "INFO", "DEBUG"],
|
| 1746 |
-
label="Debug Level",
|
| 1747 |
-
value="INFO"
|
| 1748 |
-
)
|
| 1749 |
|
| 1750 |
-
#
|
| 1751 |
-
|
| 1752 |
-
warning_count = gr.Number(value=0, label="Warnings", precision=0)
|
| 1753 |
-
|
| 1754 |
-
# Stats display
|
| 1755 |
-
debug_stats_html = gr.HTML(format_debug_stats_html())
|
| 1756 |
-
|
| 1757 |
-
# Add option to enable GUI logging for all operations
|
| 1758 |
-
enable_full_logging = gr.Checkbox(label="Log All Operations to Console", value=False)
|
| 1759 |
-
|
| 1760 |
-
# Function to update stats display
|
| 1761 |
-
def update_stats_display():
|
| 1762 |
-
return format_debug_stats_html()
|
| 1763 |
-
|
| 1764 |
-
# Set up a timer to update stats every few seconds
|
| 1765 |
-
gr.on(
|
| 1766 |
-
triggers=[debug_accordion.open],
|
| 1767 |
-
fn=update_stats_display,
|
| 1768 |
-
outputs=[debug_stats_html],
|
| 1769 |
-
every=5 # Update every 5 seconds when accordion is open
|
| 1770 |
-
)
|
| 1771 |
-
|
| 1772 |
-
# Update counts periodically
|
| 1773 |
-
def update_error_counts():
|
| 1774 |
-
return gui_stats['errors'], gui_stats['warnings']
|
| 1775 |
-
|
| 1776 |
-
gr.on(
|
| 1777 |
-
triggers=[debug_accordion.open],
|
| 1778 |
-
fn=update_error_counts,
|
| 1779 |
-
outputs=[error_count, warning_count],
|
| 1780 |
-
every=2
|
| 1781 |
-
)
|
| 1782 |
-
|
| 1783 |
-
# Debug info about model
|
| 1784 |
-
with gr.Accordion("Model Information", open=False):
|
| 1785 |
-
if torch.cuda.is_available():
|
| 1786 |
-
gpu_info = f"CUDA available: {torch.cuda.device_count()} GPU(s)\n"
|
| 1787 |
-
for i in range(torch.cuda.device_count()):
|
| 1788 |
-
gpu_info += f"- GPU {i}: {torch.cuda.get_device_name(i)}\n"
|
| 1789 |
-
gpu_info += f"Total memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.2f} GB"
|
| 1790 |
-
else:
|
| 1791 |
-
gpu_info = "CUDA not available - using CPU"
|
| 1792 |
-
|
| 1793 |
-
gr.Textbox(value=gpu_info, label="GPU Information", lines=4)
|
| 1794 |
-
|
| 1795 |
-
model_info = f"Model: {MODEL_NAME}\nImage size: {IMAGE_SIZE}x{IMAGE_SIZE}"
|
| 1796 |
-
gr.Textbox(value=model_info, label="Model Configuration", lines=2)
|
| 1797 |
-
|
| 1798 |
-
# Function to get current memory usage
|
| 1799 |
-
def get_memory_usage():
|
| 1800 |
-
if torch.cuda.is_available():
|
| 1801 |
-
allocated = torch.cuda.memory_allocated() / 1e9 # GB
|
| 1802 |
-
reserved = torch.cuda.memory_reserved() / 1e9 # GB
|
| 1803 |
-
max_memory = torch.cuda.max_memory_allocated() / 1e9 # GB
|
| 1804 |
-
return f"Allocated: {allocated:.2f} GB\nReserved: {reserved:.2f} GB\nMax used: {max_memory:.2f} GB"
|
| 1805 |
-
return "No GPU available"
|
| 1806 |
-
|
| 1807 |
-
memory_usage = gr.Textbox(
|
| 1808 |
-
value=get_memory_usage(),
|
| 1809 |
-
label="Current GPU Memory Usage",
|
| 1810 |
-
lines=3
|
| 1811 |
-
)
|
| 1812 |
-
|
| 1813 |
-
# Refresh memory usage
|
| 1814 |
-
refresh_memory_btn = gr.Button("Refresh Memory Info")
|
| 1815 |
-
refresh_memory_btn.click(
|
| 1816 |
-
fn=get_memory_usage,
|
| 1817 |
-
inputs=[],
|
| 1818 |
-
outputs=[memory_usage]
|
| 1819 |
-
)
|
| 1820 |
|
| 1821 |
-
|
| 1822 |
-
|
| 1823 |
-
|
| 1824 |
-
def get_log_file_path():
|
| 1825 |
-
return log_file if os.path.exists(log_file) else None
|
| 1826 |
-
|
| 1827 |
-
download_log_btn = gr.Button("Download Full Log File")
|
| 1828 |
-
log_file_output = gr.File(label="Log File for Download")
|
| 1829 |
-
|
| 1830 |
-
download_log_btn.click(
|
| 1831 |
-
fn=get_log_file_path,
|
| 1832 |
inputs=[],
|
| 1833 |
-
outputs=[
|
| 1834 |
)
|
| 1835 |
-
|
| 1836 |
-
|
| 1837 |
-
|
| 1838 |
-
if level == "ERROR":
|
| 1839 |
-
gui_log_handler.setLevel(logging.ERROR)
|
| 1840 |
-
logger.info(f"Debug display log level set to ERROR")
|
| 1841 |
-
elif level == "WARNING":
|
| 1842 |
-
gui_log_handler.setLevel(logging.WARNING)
|
| 1843 |
-
logger.info(f"Debug display log level set to WARNING")
|
| 1844 |
-
elif level == "INFO":
|
| 1845 |
-
gui_log_handler.setLevel(logging.INFO)
|
| 1846 |
-
logger.info(f"Debug display log level set to INFO")
|
| 1847 |
-
else: # DEBUG
|
| 1848 |
-
gui_log_handler.setLevel(logging.DEBUG)
|
| 1849 |
-
logger.info(f"Debug display log level set to DEBUG")
|
| 1850 |
-
return f"Log level set to {level}"
|
| 1851 |
-
|
| 1852 |
-
debug_level.change(
|
| 1853 |
-
fn=change_log_level,
|
| 1854 |
-
inputs=[debug_level],
|
| 1855 |
-
outputs=[]
|
| 1856 |
-
)
|
| 1857 |
-
|
| 1858 |
-
# Button handlers
|
| 1859 |
-
clear_btn.click(
|
| 1860 |
-
fn=clear_logs,
|
| 1861 |
-
inputs=[],
|
| 1862 |
-
outputs=[debug_output]
|
| 1863 |
-
)
|
| 1864 |
-
|
| 1865 |
-
refresh_btn.click(
|
| 1866 |
-
fn=get_debug_logs,
|
| 1867 |
-
inputs=[],
|
| 1868 |
-
outputs=[debug_output]
|
| 1869 |
-
)
|
| 1870 |
-
|
| 1871 |
-
# Set up automatic refresh of debug logs
|
| 1872 |
-
debug_output.change(
|
| 1873 |
-
fn=update_logs,
|
| 1874 |
-
inputs=[debug_output],
|
| 1875 |
-
outputs=[debug_output],
|
| 1876 |
-
every=1 # Update every second
|
| 1877 |
-
)
|
| 1878 |
|
| 1879 |
# Main tabs for functionality
|
| 1880 |
with gr.Tabs():
|
|
|
|
| 24 |
import queue
|
| 25 |
from typing import List, Dict, Any
|
| 26 |
|
| 27 |
+
# SIMPLIFIED LOGGING SETUP - Very direct approach
|
| 28 |
+
# Create output directory if it doesn't exist
|
| 29 |
+
os.makedirs("saved_outputs", exist_ok=True)
|
| 30 |
+
|
| 31 |
+
# Set up basic logging to both file and console
|
| 32 |
+
log_file = f"saved_outputs/debug_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
|
| 33 |
+
logging.basicConfig(
|
| 34 |
+
level=logging.DEBUG,
|
| 35 |
+
format='%(asctime)s [%(levelname)s] %(message)s',
|
| 36 |
+
handlers=[
|
| 37 |
+
logging.FileHandler(log_file),
|
| 38 |
+
logging.StreamHandler(sys.stdout)
|
| 39 |
+
]
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
# Create a logger
|
| 43 |
+
logger = logging.getLogger("internvl_app")
|
| 44 |
+
logger.setLevel(logging.DEBUG)
|
| 45 |
+
|
| 46 |
+
# Log startup information
|
| 47 |
+
logger.info("="*50)
|
| 48 |
+
logger.info("InternVL2.5 Image Analyzer starting up")
|
| 49 |
+
logger.info(f"Log file: {log_file}")
|
| 50 |
+
logger.info(f"Python version: {sys.version}")
|
| 51 |
+
logger.info(f"Torch version: {torch.__version__}")
|
| 52 |
+
logger.info(f"CUDA available: {torch.cuda.is_available()}")
|
| 53 |
+
if torch.cuda.is_available():
|
| 54 |
+
logger.info(f"CUDA version: {torch.version.cuda}")
|
| 55 |
+
logger.info(f"GPU: {torch.cuda.get_device_name(0)}")
|
| 56 |
+
logger.info("="*50)
|
| 57 |
+
|
| 58 |
+
# In-memory stats
|
| 59 |
+
error_count = 0
|
| 60 |
+
warning_count = 0
|
| 61 |
+
last_error = "None"
|
| 62 |
+
last_error_time = ""
|
| 63 |
|
| 64 |
# Constants
|
| 65 |
IMAGENET_MEAN = (0.485, 0.456, 0.406)
|
|
|
|
| 1111 |
# Function to analyze images with a prompt for folder analysis
|
| 1112 |
def analyze_folder_images(folder_path, prompt):
|
| 1113 |
"""Analyze all images in a folder."""
|
| 1114 |
+
# Add direct print output for high visibility
|
| 1115 |
+
print(f"\n\n===== FOLDER ANALYSIS STARTED =====")
|
| 1116 |
+
print(f"Folder path: {folder_path}")
|
| 1117 |
+
print(f"Prompt: {prompt}")
|
| 1118 |
+
print(f"Current directory: {os.getcwd()}")
|
| 1119 |
+
print(f"Directory exists: {os.path.exists(folder_path)}")
|
| 1120 |
+
|
| 1121 |
+
# Log to file system
|
| 1122 |
logger.info(f"analyze_folder_images called with path: '{folder_path}'")
|
| 1123 |
|
|
|
|
|
|
|
|
|
|
| 1124 |
if not folder_path or folder_path.strip() == "":
|
| 1125 |
error_msg = "No folder path provided. Please enter a valid folder path."
|
| 1126 |
logger.error(error_msg)
|
| 1127 |
+
print(f"ERROR: {error_msg}")
|
|
|
|
|
|
|
| 1128 |
return error_msg
|
| 1129 |
|
| 1130 |
# Clean up the folder path
|
|
|
|
| 1144 |
if os.path.exists("/data"):
|
| 1145 |
potential_paths.append(os.path.join("/data", folder_path))
|
| 1146 |
|
| 1147 |
+
# Print all potential paths for debugging
|
| 1148 |
+
print(f"Trying the following paths:")
|
| 1149 |
+
for i, path in enumerate(potential_paths):
|
| 1150 |
+
print(f" {i+1}. {path} (exists: {os.path.exists(path)})")
|
|
|
|
|
|
|
| 1151 |
|
| 1152 |
# Try each path
|
| 1153 |
valid_path = None
|
|
|
|
| 1158 |
if os.path.isdir(test_path):
|
| 1159 |
valid_path = test_path
|
| 1160 |
logger.info(f"Found valid directory path: '{valid_path}'")
|
| 1161 |
+
print(f"FOUND VALID PATH: {valid_path}")
|
| 1162 |
break
|
| 1163 |
else:
|
| 1164 |
logger.debug(f"Path exists but is not a directory: '{test_path}'")
|
|
|
|
| 1166 |
if not valid_path:
|
| 1167 |
error_msg = f"Could not find a valid directory at '{folder_path}'. Please provide a complete and valid folder path."
|
| 1168 |
logger.error(error_msg)
|
| 1169 |
+
print(f"ERROR: {error_msg}")
|
|
|
|
|
|
|
| 1170 |
|
| 1171 |
# Try to provide helpful information about available directories
|
| 1172 |
try:
|
| 1173 |
available_dirs = [d for d in os.listdir('.') if os.path.isdir(d)]
|
| 1174 |
+
print(f"Available directories in current location: {', '.join(available_dirs)}")
|
| 1175 |
if available_dirs:
|
| 1176 |
return f"Error: {error_msg}\n\nAvailable directories in current location: {', '.join(available_dirs)}"
|
| 1177 |
else:
|
| 1178 |
return f"Error: {error_msg}\n\nNo directories found in the current location."
|
| 1179 |
+
except Exception as list_err:
|
| 1180 |
+
print(f"Error listing directories: {str(list_err)}")
|
| 1181 |
return f"Error: {error_msg}"
|
| 1182 |
|
| 1183 |
# Convert to Path object for easier handling
|
|
|
|
| 1188 |
image_files = []
|
| 1189 |
for ext in SUPPORTED_EXTENSIONS:
|
| 1190 |
logger.debug(f"Searching for files with extension: {ext}")
|
| 1191 |
+
print(f"Searching for *{ext} files")
|
| 1192 |
# Use glob patterns that are case-insensitive
|
| 1193 |
+
found_files = list(folder_path.glob(f"*{ext.lower()}"))
|
| 1194 |
+
found_files.extend(list(folder_path.glob(f"*{ext.upper()}")))
|
| 1195 |
+
image_files.extend(found_files)
|
| 1196 |
+
print(f"Found {len(found_files)} files with extension {ext}")
|
| 1197 |
|
| 1198 |
logger.info(f"Found {len(image_files)} images in {folder_path}")
|
| 1199 |
+
print(f"Total files found: {len(image_files)}")
|
| 1200 |
|
| 1201 |
if not image_files:
|
| 1202 |
error_msg = f"No supported image files found in '{folder_path}'. Supported formats: {', '.join(SUPPORTED_EXTENSIONS)}"
|
| 1203 |
logger.warning(error_msg)
|
| 1204 |
+
print(f"WARNING: {error_msg}")
|
|
|
|
|
|
|
| 1205 |
return error_msg
|
| 1206 |
|
| 1207 |
# Sort the files for consistent output
|
| 1208 |
image_files.sort()
|
| 1209 |
|
| 1210 |
+
# Print filenames for debugging
|
| 1211 |
+
print("Files to process:")
|
| 1212 |
+
for i, file in enumerate(image_files):
|
| 1213 |
+
print(f" {i+1}. {file.name}")
|
| 1214 |
+
|
| 1215 |
results = []
|
| 1216 |
results.append(f"Found {len(image_files)} images in {folder_path}\n")
|
| 1217 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1218 |
# Process each image
|
| 1219 |
for i, img_path in enumerate(image_files, 1):
|
| 1220 |
try:
|
| 1221 |
logger.info(f"Processing image {i}/{len(image_files)}: {img_path.name}")
|
| 1222 |
+
print(f"\nProcessing file {i}/{len(image_files)}: {img_path.name}")
|
| 1223 |
|
| 1224 |
# Check if file is a PDF
|
| 1225 |
is_pdf = img_path.suffix.lower() == '.pdf'
|
| 1226 |
|
| 1227 |
if is_pdf:
|
| 1228 |
logger.info(f"Processing PDF file: {img_path}")
|
| 1229 |
+
print(f"This is a PDF file: {img_path}")
|
| 1230 |
try:
|
| 1231 |
# Process PDF pages separately
|
| 1232 |
logger.debug(f"Converting PDF to images: {img_path}")
|
| 1233 |
+
print(f"Converting PDF to images...")
|
| 1234 |
+
|
| 1235 |
+
# Check if file exists and can be read
|
| 1236 |
+
if not os.path.exists(img_path):
|
| 1237 |
+
raise FileNotFoundError(f"PDF file not found: {img_path}")
|
| 1238 |
+
|
| 1239 |
+
# Check file size
|
| 1240 |
+
file_size = os.path.getsize(img_path) / 1024 # KB
|
| 1241 |
+
print(f"PDF file size: {file_size:.2f} KB")
|
| 1242 |
+
|
| 1243 |
+
try:
|
| 1244 |
+
# Read a few bytes to check file format
|
| 1245 |
+
with open(img_path, 'rb') as f:
|
| 1246 |
+
header = f.read(10)
|
| 1247 |
+
print(f"File header (hex): {' '.join([f'{b:02x}' for b in header])}")
|
| 1248 |
+
if not header.startswith(b'%PDF'):
|
| 1249 |
+
print(f"WARNING: File does not have PDF header")
|
| 1250 |
+
except Exception as read_err:
|
| 1251 |
+
print(f"Error reading file header: {str(read_err)}")
|
| 1252 |
+
|
| 1253 |
+
# Try to convert the PDF
|
| 1254 |
+
try:
|
| 1255 |
+
pdf_images = convert_from_path(str(img_path))
|
| 1256 |
+
print(f"PDF converted to {len(pdf_images)} pages")
|
| 1257 |
+
except Exception as pdf_err:
|
| 1258 |
+
print(f"Error converting PDF: {str(pdf_err)}")
|
| 1259 |
+
print(traceback.format_exc())
|
| 1260 |
+
raise
|
| 1261 |
|
| 1262 |
if not pdf_images or len(pdf_images) == 0:
|
| 1263 |
error_msg = f"PDF conversion failed for {img_path.name}: No pages extracted"
|
| 1264 |
logger.error(error_msg)
|
| 1265 |
+
print(f"ERROR: {error_msg}")
|
|
|
|
|
|
|
| 1266 |
results.append(f"---\nImage {i}/{len(image_files)}: {img_path.name}\nError: PDF conversion failed - no pages extracted\n")
|
| 1267 |
continue
|
| 1268 |
|
|
|
|
| 1273 |
for page_num, page_img in enumerate(pdf_images, 1):
|
| 1274 |
try:
|
| 1275 |
logger.debug(f"Processing PDF page {page_num}/{len(pdf_images)}")
|
| 1276 |
+
print(f"Processing PDF page {page_num}/{len(pdf_images)}")
|
| 1277 |
page_prompt = f"PDF {img_path.name} - Page {page_num}/{len(pdf_images)}: {prompt}"
|
| 1278 |
page_result = process_image_with_text(page_img, page_prompt)
|
| 1279 |
pdf_results.append(f"-- Page {page_num} --\n{page_result}")
|
|
|
|
| 1281 |
error_msg = f"Error processing PDF page {page_num}: {str(page_err)}"
|
| 1282 |
logger.error(error_msg)
|
| 1283 |
logger.error(traceback.format_exc())
|
| 1284 |
+
print(f"ERROR: {error_msg}")
|
| 1285 |
+
print(traceback.format_exc())
|
|
|
|
| 1286 |
pdf_results.append(f"-- Page {page_num} --\nError: {str(page_err)}")
|
| 1287 |
|
| 1288 |
# Add all PDF results
|
|
|
|
| 1293 |
error_msg = f"Error processing PDF {img_path.name}: {str(pdf_err)}"
|
| 1294 |
logger.error(error_msg)
|
| 1295 |
logger.error(traceback.format_exc())
|
| 1296 |
+
print(f"ERROR: {error_msg}")
|
| 1297 |
+
print(traceback.format_exc())
|
|
|
|
| 1298 |
results.append(f"---\nImage {i}/{len(image_files)}: {img_path.name}\nError processing PDF: {str(pdf_err)}\n")
|
| 1299 |
else:
|
| 1300 |
# Standard image processing
|
| 1301 |
try:
|
| 1302 |
# Open and process the image
|
| 1303 |
+
print(f"Processing regular image file")
|
| 1304 |
image = Image.open(img_path).convert('RGB')
|
| 1305 |
logger.debug(f"Image loaded: size={image.size}, mode={image.mode}")
|
| 1306 |
+
print(f"Image loaded: size={image.size}, mode={image.mode}")
|
| 1307 |
|
| 1308 |
# Process image
|
| 1309 |
image_prompt = f"Image {i}/{len(image_files)} - {img_path.name}: {prompt}"
|
|
|
|
| 1315 |
|
| 1316 |
# Log success
|
| 1317 |
logger.info(f"Successfully processed image {i}/{len(image_files)}: {img_path.name}")
|
| 1318 |
+
print(f"Successfully processed image {i}/{len(image_files)}: {img_path.name}")
|
| 1319 |
except Exception as img_err:
|
| 1320 |
error_msg = f"Error opening/processing image {img_path.name}: {str(img_err)}"
|
| 1321 |
logger.error(error_msg)
|
| 1322 |
logger.error(traceback.format_exc())
|
| 1323 |
+
print(f"ERROR: {error_msg}")
|
| 1324 |
+
print(traceback.format_exc())
|
|
|
|
| 1325 |
results.append(f"---\nImage {i}/{len(image_files)}: {img_path.name}\nError opening/processing image: {str(img_err)}\n")
|
| 1326 |
|
| 1327 |
except Exception as e:
|
| 1328 |
error_msg = f"Error processing image {img_path.name}: {str(e)}"
|
| 1329 |
logger.error(error_msg)
|
| 1330 |
logger.error(traceback.format_exc())
|
| 1331 |
+
print(f"ERROR: {error_msg}")
|
| 1332 |
+
print(traceback.format_exc())
|
|
|
|
| 1333 |
results.append(f"---\nImage {i}/{len(image_files)}: {img_path.name}\nError: {str(e)}\n")
|
| 1334 |
|
| 1335 |
+
print("===== FOLDER ANALYSIS COMPLETE =====\n\n")
|
| 1336 |
combined_result = "\n".join(results)
|
| 1337 |
logger.info(f"Folder analysis complete, processed {len(image_files)} images")
|
| 1338 |
return combined_result
|
|
|
|
| 1613 |
|
| 1614 |
# Main function
|
| 1615 |
def main():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1616 |
# Load the model
|
| 1617 |
model, tokenizer = load_model()
|
| 1618 |
|
|
|
|
| 1638 |
"Summarize what you see in this image in one paragraph."
|
| 1639 |
]
|
| 1640 |
|
| 1641 |
+
# Simple function to read log file
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1642 |
def read_log_file():
|
| 1643 |
try:
|
| 1644 |
+
with open(log_file, 'r') as f:
|
| 1645 |
return f.read()
|
| 1646 |
except Exception as e:
|
| 1647 |
return f"Error reading log file: {str(e)}"
|
| 1648 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1649 |
# Create tabs for different modes
|
| 1650 |
with gr.Blocks(title="InternVL2.5 Image Analyzer", theme=gr.themes.Soft()) as demo:
|
| 1651 |
gr.Markdown("# InternVL2.5 Image Analyzer")
|
| 1652 |
gr.Markdown("Analyze images using the InternVL2.5 model. You can upload individual images or analyze all images in a folder.")
|
| 1653 |
|
| 1654 |
+
# Add a simple logs view tab first
|
| 1655 |
+
with gr.Tab("Debug Logs"):
|
| 1656 |
gr.Markdown("## View Application Logs")
|
|
|
|
| 1657 |
|
| 1658 |
with gr.Row():
|
| 1659 |
+
with gr.Column(scale=3):
|
| 1660 |
+
logs_output = gr.Textbox(
|
| 1661 |
+
label="Application Logs",
|
| 1662 |
value=read_log_file(),
|
| 1663 |
+
lines=30,
|
| 1664 |
+
max_lines=50
|
|
|
|
| 1665 |
)
|
| 1666 |
with gr.Column(scale=1):
|
| 1667 |
refresh_logs_btn = gr.Button("Refresh Logs")
|
| 1668 |
+
log_info = gr.Markdown(f"Log file: {log_file}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1669 |
|
| 1670 |
+
# Simple error stats
|
| 1671 |
+
error_stats = gr.Markdown(f"Error count: {error_count}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1672 |
|
| 1673 |
+
refresh_logs_btn.click(
|
| 1674 |
+
fn=read_log_file,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1675 |
inputs=[],
|
| 1676 |
+
outputs=[logs_output]
|
| 1677 |
)
|
| 1678 |
+
|
| 1679 |
+
# Add download button for log file
|
| 1680 |
+
gr.File(label="Download Log File", value=log_file)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1681 |
|
| 1682 |
# Main tabs for functionality
|
| 1683 |
with gr.Tabs():
|