120 lines
4.7 KiB
Julia
120 lines
4.7 KiB
Julia
# BRIDGE SCRIPT: Connects JuliaMSI framework to the CNN Preprocessing pipeline.
|
|
# VERSION (V3): Relying directly on precomputed analytics and multiple mz slices for max throughtput
|
|
|
|
using Pkg
|
|
juliamsi_path = "/app/JuliaMSI"
|
|
push!(LOAD_PATH, joinpath(juliamsi_path, "src"))
|
|
|
|
# Load JuliaMSI core
|
|
include(joinpath(juliamsi_path, "src", "MSI_src.jl"))
|
|
|
|
using .MSI_src
|
|
using Statistics, Images, Interpolations, BenchmarkTools
|
|
|
|
# CONFIGURATION
|
|
data_path = "/app/data/Leaf.imzML"
|
|
mask_path = "/app/data/region_of_interest.png"
|
|
output_folder = "/app/datos_para_ai"
|
|
img_size = (256, 256)
|
|
tolerance = 0.05
|
|
mkpath(output_folder)
|
|
|
|
println("--- JULIA BATCH ENGINE ---")
|
|
|
|
function run_production_pipeline()
|
|
# 1. LOAD DATA
|
|
println("Step 1: Ingesting Data...")
|
|
data = OpenMSIData(data_path)
|
|
|
|
orig_height = data.image_dims[2]
|
|
orig_width = data.image_dims[1]
|
|
if orig_height < 256 || orig_width < 256
|
|
final_img_size = (orig_height, orig_width)
|
|
println(" [Notice] Original image is smaller than 256x256. Keeping original size: ", final_img_size)
|
|
else
|
|
final_img_size = (256, 256)
|
|
println(" [Notice] Resizing down to target size: ", final_img_size)
|
|
end
|
|
|
|
# 2. RUN FRAMEWORK ANALYTICS
|
|
println("Step 2: Checking Dataset Precomputed Boundaries...")
|
|
# This matches the framework's internal logic to safely discover the true boundaries.
|
|
# It populates global_min_mz and global_max_mz by reading the binary headers properly.
|
|
MSI_src.precompute_analytics(data)
|
|
|
|
# 3. EXTRACT AVERAGE SPECTRUM
|
|
println("Step 3: Calculating ROI Average Spectrum...")
|
|
mzs, avg_ints = MSI_src.get_average_spectrum(data, mask_path=mask_path)
|
|
|
|
println(" True Dataset Min m/z: ", round(minimum(mzs), digits=2))
|
|
println(" True Dataset Max m/z: ", round(maximum(mzs), digits=2))
|
|
|
|
# 4. PEAK PICKING USING TRUE SPECTRA LIMITS
|
|
# Identify relevant ion channels above a 2% intensity base peak threshold
|
|
threshold = maximum(avg_ints) * 0.02
|
|
peak_indices = findall(x -> x > threshold, avg_ints)
|
|
|
|
min_vis_mz = data.global_min_mz[] > 0.0 ? data.global_min_mz[] : 50.0
|
|
max_vis_mz = data.global_max_mz[] > 0.0 ? data.global_max_mz[] : 1000.0
|
|
|
|
# Isolate targets that fall strictly within the standard CNN metabolic training window
|
|
target_masses = Float64[]
|
|
for idx in peak_indices
|
|
mz_val = mzs[idx]
|
|
if mz_val >= min_vis_mz && mz_val <= max_vis_mz
|
|
push!(target_masses, mz_val)
|
|
end
|
|
end
|
|
|
|
total_ions = length(target_masses)
|
|
println("Found $total_ions ions within training range ($(min_vis_mz) to $(max_vis_mz) m/z) above threshold.")
|
|
if total_ions == 0
|
|
println("Warning: No peaks met the criteria. Check your threshold or mask alignment.")
|
|
return
|
|
end
|
|
|
|
# 5. MULTI-CHANNEL BATCH EXTRACTION (Single pass over binary data)
|
|
println("Step 4: Multi-channel Batch Extraction (Optimized I/O)...")
|
|
slice_dict = MSI_src.get_multiple_mz_slices(data, target_masses, tolerance, mask_path=mask_path)
|
|
|
|
# 6. THREADED PROCESSING & EXPORT
|
|
println("Step 5: Threaded TrIQ Normalization & Safe 256x256 Zero-Padding...")
|
|
mask_matrix = MSI_src.load_and_prepare_mask(mask_path, (data.image_dims[1], data.image_dims[2]))
|
|
exported_count = Threads.Atomic{Int}(0)
|
|
|
|
# Fixed target dimensions for the CNN
|
|
target_size = (256, 256)
|
|
|
|
Threads.@threads for mz_val in target_masses
|
|
slice = get(slice_dict, mz_val, nothing)
|
|
if slice === nothing || all(iszero, slice) continue end
|
|
|
|
# 1. Framework-Native TrIQ (0.0 to 255.0 Scaling)
|
|
slice_quant, _ = MSI_src.TrIQ(slice, 256, 0.98, mask_matrix=mask_matrix)
|
|
|
|
# 2. Allocate an empty 256x256 background matrix
|
|
padded_matrix = zeros(Float32, target_size)
|
|
|
|
# 3. Compute structural centering offsets based on true resolution (161x106)
|
|
h_orig, w_orig = size(slice_quant, 1), size(slice_quant, 2)
|
|
offset_y = div(target_size[1] - h_orig, 2) + 1
|
|
offset_x = div(target_size[2] - w_orig, 2) + 1
|
|
|
|
# 4. Map the pristine, raw pixels directly into the center
|
|
padded_matrix[offset_y:offset_y+h_orig-1, offset_x:offset_x+w_orig-1] .= slice_quant
|
|
|
|
# 5. Convert safely to 0.0 - 1.0 float mapping for Gray image export
|
|
normalized_gray = padded_matrix ./ 255.0
|
|
|
|
# 6. Save the unblurred, structurally perfect map
|
|
filename = "ion_$(round(mz_val, digits=2)).png"
|
|
save(joinpath(output_folder, filename), Gray.(normalized_gray))
|
|
|
|
Threads.atomic_add!(exported_count, 1)
|
|
end
|
|
|
|
println("\nSuccess: $(exported_count[]) target image maps exported to $output_folder.")
|
|
end
|
|
|
|
@time run_production_pipeline()
|