# import gradio as gr # import pandas as pd # import os # from pathlib import Path # from datetime import datetime # import random # from PIL import Image # # Global state # state = { # "session_active": False, # "image_pairs": [], # "current_index": 0, # "evaluations": [], # "doctor_name": "", # } # # Helper functions # def get_image_files(folder_path): # """Get all image files from a folder.""" # valid_extensions = {'.jpg', '.jpeg', '.png', '.gif'} # files = {} # if os.path.exists(folder_path): # for file in os.listdir(folder_path): # if Path(file).suffix.lower() in valid_extensions: # files[file] = file # return files # def create_image_pairs(): # """Create paired images from roentgen and pedisynth folders.""" # roentgen_files = get_image_files("roentgen") # pedisynth_files = get_image_files("pedisynth") # pairs = [] # # Match files by exact filename across folders # for filename in roentgen_files: # if filename in pedisynth_files: # # Randomly decide which image goes left and which goes right # is_roentgen_left = random.choice([True, False]) # # Extract condition by removing the file extension and last part (number) # name_without_ext = Path(filename).stem # parts = name_without_ext.rsplit(' ', 1) # condition = parts[0] if len(parts) > 1 else name_without_ext # pairs.append({ # 'roentgen': filename, # 'pedisynth': filename, # 'condition': condition, # 'base_name': name_without_ext, # 'roentgen_left': is_roentgen_left # }) # # Randomize order # random.shuffle(pairs) # return pairs # def load_image(folder, filename): # """Load image from folder.""" # filepath = os.path.join(folder, filename) # try: # return Image.open(filepath) # except Exception as e: # print(f"Error loading image {filename}: {e}") # return None # def save_evaluations(evaluations, doctor_name): # """Save evaluations to CSV.""" # df = pd.DataFrame(evaluations) # timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") # filename = f"evaluations_{doctor_name}_{timestamp}.csv" # # Save to outputs folder if it exists, otherwise current directory # output_dir = "outputs" # os.makedirs(output_dir, exist_ok=True) # filepath = os.path.join(output_dir, filename) # df.to_csv(filepath, index=False) # return filepath, filename # def start_session(doctor_name): # """Start a new evaluation session.""" # if not doctor_name.strip(): # return (gr.update(visible=False), "Please enter your name to start a session.") # state["doctor_name"] = doctor_name # state["image_pairs"] = create_image_pairs() # state["session_active"] = True # state["current_index"] = 0 # state["evaluations"] = [] # if not state["image_pairs"]: # return (gr.update(visible=False), "No image pairs found. Please ensure both roentgen/ and pedisynth/ folders have matching images.") # return (gr.update(visible=True), "") # def get_preference_a(): # """Handle preference for option A.""" # if not state["session_active"]: # return None, None, "Session not active." # current_pair = state["image_pairs"][state["current_index"]] # preferred_source = state["current_left_source"] # state["evaluations"].append({ # 'timestamp': datetime.now().isoformat(), # 'condition': current_pair['condition'], # 'base_name': current_pair['base_name'], # 'preference': preferred_source.capitalize(), # 'doctor': state["doctor_name"] # }) # state["current_index"] += 1 # if state["current_index"] >= len(state["image_pairs"]): # state["session_active"] = False # return None, None, "✅ Evaluation complete! All images have been reviewed." # current_pair = state["image_pairs"][state["current_index"]] # if current_pair['roentgen_left']: # left_image = load_image("roentgen", current_pair['roentgen']) # right_image = load_image("pedisynth", current_pair['pedisynth']) # state["current_left_source"] = "roentgen" # state["current_right_source"] = "pedisynth" # else: # left_image = load_image("pedisynth", current_pair['pedisynth']) # right_image = load_image("roentgen", current_pair['roentgen']) # state["current_left_source"] = "pedisynth" # state["current_right_source"] = "roentgen" # progress = f"Image {state['current_index'] + 1} of {len(state['image_pairs'])} | Condition: {current_pair['condition']}" # return left_image, right_image, progress # def get_preference_b(): # """Handle preference for option B.""" # if not state["session_active"]: # return None, None, "Session not active." # current_pair = state["image_pairs"][state["current_index"]] # preferred_source = state["current_right_source"] # state["evaluations"].append({ # 'timestamp': datetime.now().isoformat(), # 'condition': current_pair['condition'], # 'base_name': current_pair['base_name'], # 'preference': preferred_source.capitalize(), # 'doctor': state["doctor_name"] # }) # state["current_index"] += 1 # if state["current_index"] >= len(state["image_pairs"]): # state["session_active"] = False # return None, None, "✅ Evaluation complete! All images have been reviewed." # current_pair = state["image_pairs"][state["current_index"]] # if current_pair['roentgen_left']: # left_image = load_image("roentgen", current_pair['roentgen']) # right_image = load_image("pedisynth", current_pair['pedisynth']) # state["current_left_source"] = "roentgen" # state["current_right_source"] = "pedisynth" # else: # left_image = load_image("pedisynth", current_pair['pedisynth']) # right_image = load_image("roentgen", current_pair['roentgen']) # state["current_left_source"] = "pedisynth" # state["current_right_source"] = "roentgen" # progress = f"Image {state['current_index'] + 1} of {len(state['image_pairs'])} | Condition: {current_pair['condition']}" # return left_image, right_image, progress # def display_first_pair(): # """Display the first image pair after session starts.""" # if not state["session_active"]: # return None, None, "" # current_pair = state["image_pairs"][state["current_index"]] # if current_pair['roentgen_left']: # left_image = load_image("roentgen", current_pair['roentgen']) # right_image = load_image("pedisynth", current_pair['pedisynth']) # state["current_left_source"] = "roentgen" # state["current_right_source"] = "pedisynth" # else: # left_image = load_image("pedisynth", current_pair['pedisynth']) # right_image = load_image("roentgen", current_pair['roentgen']) # state["current_left_source"] = "pedisynth" # state["current_right_source"] = "roentgen" # progress = f"Image {state['current_index'] + 1} of {len(state['image_pairs'])} | Condition: {current_pair['condition']}" # return left_image, right_image, progress # def end_session(): # """End the session and return CSV file.""" # if not state["evaluations"]: # return None, "No evaluations recorded." # filepath, filename = save_evaluations(state["evaluations"], state["doctor_name"]) # state["session_active"] = False # state["current_index"] = 0 # state["evaluations"] = [] # return filepath, f"✅ Evaluations saved as: {filename}" # # Gradio interface # with gr.Blocks(title="X-Ray Quality Evaluation") as demo: # gr.Markdown("# 🏥 X-Ray Quality Evaluation System") # gr.Markdown("**Pairwise Preference Test for Synthetic X-Ray Images**") # with gr.Row(): # with gr.Column(scale=1): # gr.Markdown("### Start Session") # doctor_name_input = gr.Textbox( # label="Enter your name:", # placeholder="Dr. Smith" # ) # start_btn = gr.Button("Start Session", variant="primary", size="lg") # with gr.Column(scale=1): # gr.Markdown("### Results") # download_btn = gr.Button("End Session & Download Results", variant="primary", size="lg") # result_message = gr.Textbox( # label="Status", # interactive=False, # show_label=True # ) # error_section = gr.Textbox( # label="Message", # interactive=False, # show_label=True, # value="" # ) # # Image comparison section # with gr.Group(visible=False) as eval_section: # gr.Markdown("### Compare Images") # progress_text = gr.Textbox( # label="Progress", # interactive=False, # show_label=True # ) # with gr.Row(): # image_a = gr.Image( # label="Option A", # show_label=True, # type="pil" # ) # image_b = gr.Image( # label="Option B", # show_label=True, # type="pil" # ) # with gr.Row(): # pref_a = gr.Button("👈 Prefer Option A", size="lg") # pref_b = gr.Button("👉 Prefer Option B", size="lg") # # File download # download_file = gr.File( # label="CSV Results", # visible=False # ) # # Event handlers # start_btn.click( # start_session, # inputs=doctor_name_input, # outputs=[eval_section, error_section] # ).then( # display_first_pair, # outputs=[image_a, image_b, progress_text] # ) # pref_a.click( # get_preference_a, # outputs=[image_a, image_b, progress_text] # ) # pref_b.click( # get_preference_b, # outputs=[image_a, image_b, progress_text] # ) # download_btn.click( # end_session, # outputs=[download_file, result_message] # ) # if __name__ == "__main__": # demo.launch() import gradio as gr import pandas as pd import os from pathlib import Path from datetime import datetime import random from PIL import Image # Global state state = { "session_active": False, "image_pairs": [], "current_index": 0, "evaluations": [], "doctor_name": "", } # Helper functions def get_image_files(folder_path): """Get all image files from a folder.""" valid_extensions = {'.jpg', '.jpeg', '.png', '.gif'} files = {} if os.path.exists(folder_path): for file in os.listdir(folder_path): if Path(file).suffix.lower() in valid_extensions: files[file] = file return files def create_image_pairs(): """Create paired images from roentgen and pedisynth folders.""" roentgen_files = get_image_files("roentgen") pedisynth_files = get_image_files("pedisynth") pairs = [] # Match files by exact filename across folders for filename in roentgen_files: if filename in pedisynth_files: # Randomly decide which image goes left and which goes right is_roentgen_left = random.choice([True, False]) # Extract condition by removing the file extension and last part (number) name_without_ext = Path(filename).stem parts = name_without_ext.rsplit(' ', 1) condition = parts[0] if len(parts) > 1 else name_without_ext pairs.append({ 'roentgen': filename, 'pedisynth': filename, 'condition': condition, 'base_name': name_without_ext, 'roentgen_left': is_roentgen_left }) # Randomize order random.shuffle(pairs) return pairs def load_image(folder, filename): """Load image from folder.""" filepath = os.path.join(folder, filename) try: return Image.open(filepath) except Exception as e: print(f"Error loading image {filename}: {e}") return None def save_evaluations(evaluations, doctor_name): """Save evaluations to CSV.""" df = pd.DataFrame(evaluations) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"evaluations_{doctor_name}_{timestamp}.csv" # Save to outputs folder if it exists, otherwise current directory output_dir = "outputs" os.makedirs(output_dir, exist_ok=True) filepath = os.path.join(output_dir, filename) df.to_csv(filepath, index=False) return filepath, filename def start_session(doctor_name): """Start a new evaluation session.""" if not doctor_name.strip(): return (gr.update(visible=False), "Please enter your name to start a session.") state["doctor_name"] = doctor_name state["image_pairs"] = create_image_pairs() state["session_active"] = True state["current_index"] = 0 state["evaluations"] = [] if not state["image_pairs"]: return (gr.update(visible=False), "No image pairs found. Please ensure both roentgen/ and pedisynth/ folders have matching images.") return (gr.update(visible=True), "") def get_preference_a(): """Handle preference for option A.""" if not state["session_active"]: return None, None, "Session not active." current_pair = state["image_pairs"][state["current_index"]] preferred_source = state["current_left_source"] state["evaluations"].append({ 'timestamp': datetime.now().isoformat(), 'condition': current_pair['condition'], 'base_name': current_pair['base_name'], 'preference': preferred_source.capitalize(), 'doctor': state["doctor_name"] }) state["current_index"] += 1 if state["current_index"] >= len(state["image_pairs"]): state["session_active"] = False return None, None, "✅ Evaluation complete! All images have been reviewed." current_pair = state["image_pairs"][state["current_index"]] if current_pair['roentgen_left']: left_image = load_image("roentgen", current_pair['roentgen']) right_image = load_image("pedisynth", current_pair['pedisynth']) state["current_left_source"] = "roentgen" state["current_right_source"] = "pedisynth" else: left_image = load_image("pedisynth", current_pair['pedisynth']) right_image = load_image("roentgen", current_pair['roentgen']) state["current_left_source"] = "pedisynth" state["current_right_source"] = "roentgen" progress = f"Image {state['current_index'] + 1} of {len(state['image_pairs'])} | Condition: {current_pair['condition']}" return left_image, right_image, progress def get_preference_b(): """Handle preference for option B.""" if not state["session_active"]: return None, None, "Session not active." current_pair = state["image_pairs"][state["current_index"]] preferred_source = state["current_right_source"] state["evaluations"].append({ 'timestamp': datetime.now().isoformat(), 'condition': current_pair['condition'], 'base_name': current_pair['base_name'], 'preference': preferred_source.capitalize(), 'doctor': state["doctor_name"] }) state["current_index"] += 1 if state["current_index"] >= len(state["image_pairs"]): state["session_active"] = False return None, None, "✅ Evaluation complete! All images have been reviewed." current_pair = state["image_pairs"][state["current_index"]] if current_pair['roentgen_left']: left_image = load_image("roentgen", current_pair['roentgen']) right_image = load_image("pedisynth", current_pair['pedisynth']) state["current_left_source"] = "roentgen" state["current_right_source"] = "pedisynth" else: left_image = load_image("pedisynth", current_pair['pedisynth']) right_image = load_image("roentgen", current_pair['roentgen']) state["current_left_source"] = "pedisynth" state["current_right_source"] = "roentgen" progress = f"Image {state['current_index'] + 1} of {len(state['image_pairs'])} | Condition: {current_pair['condition']}" return left_image, right_image, progress def display_first_pair(): """Display the first image pair after session starts.""" if not state["session_active"]: return None, None, "" current_pair = state["image_pairs"][state["current_index"]] if current_pair['roentgen_left']: left_image = load_image("roentgen", current_pair['roentgen']) right_image = load_image("pedisynth", current_pair['pedisynth']) state["current_left_source"] = "roentgen" state["current_right_source"] = "pedisynth" else: left_image = load_image("pedisynth", current_pair['pedisynth']) right_image = load_image("roentgen", current_pair['roentgen']) state["current_left_source"] = "pedisynth" state["current_right_source"] = "roentgen" progress = f"Image {state['current_index'] + 1} of {len(state['image_pairs'])} | Condition: {current_pair['condition']}" return left_image, right_image, progress def end_session(): """End the session and return CSV file for download.""" if not state["evaluations"]: return None, "No evaluations recorded." filepath, filename = save_evaluations(state["evaluations"], state["doctor_name"]) state["session_active"] = False state["current_index"] = 0 state["evaluations"] = [] return filepath, f"✅ Evaluations ready to download: {filename}" # Gradio interface with gr.Blocks(title="X-Ray Quality Evaluation") as demo: gr.Markdown("# 🏥 X-Ray Quality Evaluation System") gr.Markdown("**Pairwise Preference Test for Synthetic X-Ray Images**") with gr.Row(): with gr.Column(scale=1): gr.Markdown("### Start Session") doctor_name_input = gr.Textbox( label="Enter your name:", placeholder="Dr. Smith" ) start_btn = gr.Button("Start Session", variant="primary", size="lg") with gr.Column(scale=1): gr.Markdown("### Results") download_btn = gr.Button("End Session & Download Results", variant="primary", size="lg") result_message = gr.Textbox( label="Status", interactive=False, show_label=True ) error_section = gr.Textbox( label="Message", interactive=False, show_label=True, value="" ) # Image comparison section with gr.Group(visible=False) as eval_section: gr.Markdown("### Compare Images") progress_text = gr.Textbox( label="Progress", interactive=False, show_label=True ) with gr.Row(): image_a = gr.Image( label="Option A", show_label=True, type="pil" ) image_b = gr.Image( label="Option B", show_label=True, type="pil" ) with gr.Row(): pref_a = gr.Button("👈 Prefer Option A", size="lg") pref_b = gr.Button("👉 Prefer Option B", size="lg") # File download download_file = gr.File( label="CSV Results", interactive=False ) # Event handlers start_btn.click( start_session, inputs=doctor_name_input, outputs=[eval_section, error_section] ).then( display_first_pair, outputs=[image_a, image_b, progress_text] ) pref_a.click( get_preference_a, outputs=[image_a, image_b, progress_text] ) pref_b.click( get_preference_b, outputs=[image_a, image_b, progress_text] ) download_btn.click( end_session, outputs=[download_file, result_message] ) if __name__ == "__main__": demo.launch()