Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions prp-unisalento.le/LICENCE.TXT
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Copyright (c) 2025 Alessandra Gabriele

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3 changes: 3 additions & 0 deletions prp-unisalento.le/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,6 @@ Fiji must be installed separately for `.js` script.
- The `.dm4` structure is parsed using open-source tools, ensuring transparency and reproducibility.
- ElabFTW serves as the centralized metadata repository and documentation tool, enabling automation and collaboration.

## Licence

Check Licence file
72 changes: 36 additions & 36 deletions prp-unisalento.le/Script (Javascript).js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Importa le librerie necessarie
// Import the required libraries
importClass(Packages.ij.IJ);
importClass(Packages.ij.io.OpenDialog);
importClass(Packages.ij.WindowManager);
Expand All @@ -8,35 +8,35 @@ importClass(javax.swing.filechooser.FileNameExtensionFilter);
importClass(java.io.File);

try {
// --- STEP 1: Seleziona più file .dm4 ---
// --- STEP 1: Select multiple .dm4 files ---
var fileChooser = new JFileChooser();
fileChooser.setMultiSelectionEnabled(true);
fileChooser.setFileFilter(new FileNameExtensionFilter("DM4 Files", "dm4"));

var returnValue = fileChooser.showOpenDialog(null);
if (returnValue != JFileChooser.APPROVE_OPTION) {
IJ.log("Nessun file selezionato.");
throw new Error("File non selezionati");
IJ.log("No file selected.");
throw new Error("Files not selected ");
}

var selectedFiles = fileChooser.getSelectedFiles();
IJ.log("Numero di file selezionati: " + selectedFiles.length);
IJ.log("Number of files selected: " + selectedFiles.length);

// --- Loop attraverso i file selezionati ---
// --- Loop through selected files ---
for (var f = 0; f < selectedFiles.length; f++) {
var filePath = selectedFiles[f].getAbsolutePath();
IJ.log("\n--- Elaborazione file: " + filePath + " ---");
IJ.log("\n--- File processing: " + filePath + " ---");

// --- STEP 2: Apri SOLO i metadati con Bio-Formats, senza caricare l'immagine ---
// --- STEP 2: Open ONLY the metadata with Bio-Formats, without loading the image ---
IJ.run("Bio-Formats Importer", "open=[" + filePath + "] display_metadata only no_open");

// --- STEP 3: Aspetta la finestra dei metadati ---
var maxWaitTime = 5000; // Tempo massimo di attesa (5 secondi)
// --- STEP 3: Wait for the metadata window ---
var maxWaitTime = 5000; // Maximum waiting time (5 seconds)
var waitTime = 0;
var win = null;

while (waitTime < maxWaitTime) {
IJ.wait(500); // Aspetta mezzo secondo
IJ.wait(500); // Wait half a second
var windowTitles = WindowManager.getNonImageTitles();

for (var i = 0; i < windowTitles.length; i++) {
Expand All @@ -54,39 +54,39 @@ try {
}

if (win == null || !(win instanceof TextWindow)) {
IJ.log("Errore: la finestra dei metadati non è stata trovata per il file: " + filePath);
continue; // Salta questo file e passa al successivo
IJ.log("Error: the metadata window was not found for the file: " + filePath);
continue; // Skip this file and move on to the next one
}

IJ.log("Finestra dei metadati trovata: " + win.getTitle());
IJ.log("Metadata window found: " + win.getTitle());

// --- STEP 4: Leggi il contenuto della finestra dei metadati ---
// --- STEP 4: Read the contents of the metadata window ---
var textPanel = win.getTextPanel();
var metadataText = textPanel.getText();

// --- STEP 5: Filtra i metadati richiesti (Tabella Key-Value) ---
// --- STEP 5: Filter required metadata (Tabella Key-Value) ---
var lines = metadataText.split("\n");
var extractedMetadata = {
"Device Name": "Non trovato",
"Formatted Voltage": "Non trovato",
"Exposure (s)": "Non trovato",
"SizeX": "Non trovato",
"SizeZ": "Non trovato",
"Acquisition Date": "Non trovato",
"Acquisition Time": "Non trovato",
"Formatted Indicated Mag": "Non trovato",
"Pixel Size (um)": "Non trovato",
"PixelDepth": "Non trovato",
"Stage Alpha": "Non trovato",
"Stage Beta": "Non trovato",
"Stage X": "Non trovato",
"Stage Y": "Non trovato",
"Stage Z": "Non trovato"
"Device Name": "Not found",
"Formatted Voltage": " Not found ",
"Exposure (s)": " Not found ",
"SizeX": " Not found ",
"SizeZ": " Not found ",
"Acquisition Date": " Not found ",
"Acquisition Time": " Not found ",
"Formatted Indicated Mag": " Not found ",
"Pixel Size (um)": " Not found ",
"PixelDepth": " Not found ",
"Stage Alpha": " Not found ",
"Stage Beta": " Not found ",
"Stage X": " Not found ",
"Stage Y": " Not found ",
"Stage Z": " Not found "
};

for (var i = 0; i < lines.length; i++) {
var line = lines[i].trim();
var lineParts = line.split(/\t+/); // Usa la tabulazione per separare le colonne
var lineParts = line.split(/\t+/); // Use tabulation to separate columns

if (lineParts.length == 2) {
var key = lineParts[0].trim().toLowerCase();
Expand All @@ -110,14 +110,14 @@ try {
}
}

// --- STEP 6: Mostra i metadati estratti ---
IJ.log("----- METADATI ESTRATTI PER: " + filePath + " -----");
// --- STEP 6: Show extracted metadata ---
IJ.log("----- EXTRACTED METADATA FOR: " + filePath + " -----");
for (var key in extractedMetadata) {
IJ.log(key + ": " + extractedMetadata[key]);
}
IJ.log("----- FINE METADATI -----");
IJ.log("----- METADATA END -----");
}

} catch (e) {
IJ.log("Errore nella lettura dei metadati: " + e);
IJ.log("Error Reading Metadata: " + e);
}
74 changes: 37 additions & 37 deletions prp-unisalento.le/Script (Python).py
Original file line number Diff line number Diff line change
Expand Up @@ -8,93 +8,94 @@
import numpy as np
import json

# Lista aggiornata dei parametri da cercare
# Updated list of search parameters
METADATA_KEYS = [
"Device Name", "Formatted Voltage", "Exposure (s)", "Acquisition Date",
"Acquisition Time", "Formatted Indicated Mag", "Pixel Size (um)", "PixelDepth",
"Stage Alpha", "Stage Beta", "Stage X", "Stage Y", "Stage Z",
"Active Size (pixels)" # Nuovo parametro aggiunto
"Active Size (pixels)" # New parameter added
]

def find_metadata_values(metadata):
"""Cerca nei metadati i parametri richiesti e ne estrae il valore dopo i due punti."""
extracted_data = {key: "N/A" for key in METADATA_KEYS} # Inizializza con N/A
""" It searches the metadata for the required parameters and extracts the value after the colon."""
extracted_data = {key: "N/A" for key in METADATA_KEYS} # initialise with N/A

def recursive_search(data):
"""Ricorsivamente cerca le chiavi nei dizionari e liste annidate."""
""" Recursively search for keys in dictionaries and nested lists."""
if isinstance(data, dict):
for key, value in data.items():
for target_key in METADATA_KEYS:
if target_key in key: # Se il nome della chiave contiene il parametro cercato
extracted_data[target_key] = convert_value(value) # Converte i dati
recursive_search(value) # Continua la ricerca nei sotto-dizionari
if target_key in key: # If the key name contains the searched parameter
extracted_data[target_key] = convert_value(value) # Converts data
recursive_search(value) # Continue searching in sub-dictionaries
elif isinstance(data, list):
for item in data:
recursive_search(item) # Cerca in ogni elemento della lista
recursive_search(item) # Search each item in the list

recursive_search(metadata)
return extracted_data

def convert_value(value):
"""Converte i tipi NumPy in tipi standard Python per la compatibilità JSON."""
""" Converts NumPy types to standard Python types for JSON compatibility."""
if isinstance(value, np.ndarray):
return value.tolist() # Converti array NumPy in lista Python
return value.tolist() # Convert NumPy array to Python list
elif isinstance(value, (np.int64, np.uint64, np.int32, np.uint32)):
return int(value) # Converti interi NumPy in interi Python
return int(value) # Convert NumPy integers to Python integers
elif isinstance(value, (np.float64, np.float32)):
return float(value) # Converti float NumPy in float Python
return value # Restituisci il valore originale se già compatibile
return float(value) # Convertire float NumPy in float Python
return value # Return the original value if already compatible

def extract_metadata(dm4_file):
"""Estrai SOLO i metadati richiesti da un file DM4."""
""" Extract ONLY the required metadata from a DM4 file."""
try:
dm4_data = dm.fileDM(dm4_file)
metadata = dm4_data.allTags # Estrarre tutti i metadati
metadata = dm4_data.allTags # Extract all metadata

# Cerca i parametri e i valori dopo i due punti
# Search for parameters and values after the colon
filtered_metadata = find_metadata_values(metadata)
return filtered_metadata

except Exception as e:
return {"Errore": f"Errore nella lettura di {dm4_file}: {e}"}
return {"Error": f"Error reading {dm4_file}: {e}"}

def convert_to_text(metadata_dict):
"""Converte i metadati in una stringa formattata per il file di testo."""
""" Converts metadata into a formatted string for the text file."""
text_output = ""
for file_name, metadata in metadata_dict.items():
text_output += f"=== METADATI DI {file_name} ===\n\n"
text_output += f"=== METADATA OF {file_name} ===\n\n"
for key, value in metadata.items():
text_output += f"{key}: {value}\n"
text_output += "\n" + "=" * 50 + "\n\n" # Separatore tra file
text_output += "\n" + "=" * 50 + "\n\n" # Inter-file separator
return text_output

def save_results(folder_path, metadata_dict):
"""Salva i metadati sia in formato TXT che JSON."""
""" Saves metadata in both TXT and JSON format."""
output_txt = os.path.join(folder_path, "metadata_output.txt")
output_json = os.path.join(folder_path, "metadata_output.json")

try:
# Salvataggio in TXT
# Saving in TXT
with open(output_txt, "w", encoding="utf-8") as txt_file:
txt_file.write(convert_to_text(metadata_dict))

# Salvataggio in JSON con conversione automatica dei tipi
# Saving in JSON with automatic type conversion
with open(output_json, "w", encoding="utf-8") as json_file:
json.dump(metadata_dict, json_file, indent=4, ensure_ascii=False)

messagebox.showinfo("Completato", f"Metadati salvati con successo in:\n{output_txt}\n{output_json}")
messagebox.showinfo("Completed", f"Metadata saved successfully
in:\n{output_txt}\n{output_json}")
open_folder(folder_path)

except Exception as e:
messagebox.showerror("Errore", f"Impossibile salvare i file:\n{e}")
messagebox.showerror("Error", f"Impossible to save files:\n{e}")

def process_dm4_files(folder_path):
"""Legge i file DM4 nella cartella e salva i metadati."""
""" Reads the DM4 files in the folder and saves the metadata."""
metadata_dict = {}
dm4_files = glob.glob(os.path.join(folder_path, "*.dm4"))

if not dm4_files:
messagebox.showerror("Errore", "Nessun file DM4 trovato nella cartella selezionata.")
messagebox.showerror("Error", " No DM4 file found in the selected folder.")
return

for dm4_file in dm4_files:
Expand All @@ -104,28 +105,27 @@ def process_dm4_files(folder_path):
save_results(folder_path, metadata_dict)

def open_folder(path):
"""Apre la cartella dei risultati in Esplora File/Finder."""
""" Opens the results folder in File Explorer /Finder."""
try:
if os.name == 'nt': # Windows
subprocess.run(f'explorer "{path}"')
elif os.name == 'posix': # Mac/Linux
subprocess.run(['xdg-open', path] if 'linux' in sys.platform else ['open', path])
except Exception as e:
messagebox.showerror("Errore", f"Impossibile aprire la cartella:\n{e}")
messagebox.showerror("Error", f" Unable to open folder:\n{e}")

def select_folder():
"""Apre la finestra di selezione della cartella e avvia l'elaborazione."""
""" Opens the folder selection window and starts processing."""
root = tk.Tk()
root.withdraw() # Nasconde la finestra principale

folder_selected = filedialog.askdirectory(title="Seleziona la cartella con i file DM4")
root.withdraw() # Hides the main window
folder_selected = filedialog.askdirectory(title=" Select the folder with the DM4 files")

if folder_selected:
process_dm4_files(folder_selected)
else:
messagebox.showinfo("Info", "Nessuna cartella selezionata. Operazione annullata.")
messagebox.showinfo("Info", " No folder selected. Operation aborted.")

# Se eseguito con doppio click, usa la cartella dello script
# When double-clicked, it uses the script folder
if __name__ == "__main__":
select_folder()
input("\nPremi INVIO per chiudere...") # Evita la chiusura immediata
input("\nPress ENTER to close...") # Avoid immediate closure