From 2fd2b905139ae896e6009c791bfe6e4655a9f1cd Mon Sep 17 00:00:00 2001 From: Pixelguy14 Date: Mon, 13 Oct 2025 22:05:56 -0600 Subject: [PATCH] altered imzML parser to be compatible with timsconverter, small improvements on the UI, other small bug fixes done --- README.md | 2 +- app.jl | 245 +++++++++++++----- app.jl.html | 109 ++++---- julia_imzML_visual.jl | 98 +++++++- public/css/genieapp.css | 10 +- src/MSIData.jl | 72 +++++- src/ParserHelpers.jl | 22 +- src/imzML.jl | 533 ++++++++++++++++++++++++++++++++++------ test/run_tests.jl | 65 ++++- 9 files changed, 916 insertions(+), 240 deletions(-) diff --git a/README.md b/README.md index 2affa30..489bd0f 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ https://codeberg.org/LabABI/JuliaMSI ~/Downloads/JuliaMSI-main/juliamsi 2. Without entering the Julia environment, launch the project in your terminal with the following command (which works for all operating systems): ``` - julia --project=. start_MSI_GUI.jl + julia --threads auto --project=. start_MSI_GUI.jl ``` 3. After the script has finished loading, you can open a [page](http://127.0.0.1:1481/) in your browser with the web app running. diff --git a/app.jl b/app.jl index fbf02b8..e1f5100 100644 --- a/app.jl +++ b/app.jl @@ -159,6 +159,14 @@ include("./julia_imzML_visual.jl") ## Plots # Local image to plot layoutImg=PlotlyBase.Layout( + title=PlotlyBase.attr( + text="", + font=PlotlyBase.attr( + family="Roboto, Lato, sans-serif", + size=14, + color="black" + ) + ), xaxis=PlotlyBase.attr( visible=false, scaleanchor="y", @@ -185,7 +193,14 @@ include("./julia_imzML_visual.jl") @out plotlayoutImgTComp=layoutImg # Interface Plot Spectrum layoutSpectra=PlotlyBase.Layout( - title="Spectrum plot", + title=PlotlyBase.attr( + text="Spectrum plot", + font=PlotlyBase.attr( + family="Roboto, Lato, sans-serif", + size=18, + color="black" + ) + ), hovermode="closest", xaxis=PlotlyBase.attr( title="m/z", @@ -215,7 +230,14 @@ include("./julia_imzML_visual.jl") # Interface Plot Surface layoutContour=PlotlyBase.Layout( - title="2D Topographic map", + title=PlotlyBase.attr( + text="2D Topographic map", + font=PlotlyBase.attr( + family="Roboto, Lato, sans-serif", + size=18, + color="black" + ) + ), xaxis=PlotlyBase.attr( visible=false, scaleanchor="y" @@ -226,7 +248,7 @@ include("./julia_imzML_visual.jl") margin=attr(l=0,r=0,t=100,b=0,pad=0) ) # Dummy 2D surface plot - traceContour=PlotlyBase.scatter(x=Vector{Float64}(), y=Vector{Float64}(), mode="lines") + traceContour=PlotlyBase.contour(x=Vector{Float64}(), y=Vector{Float64}(), mode="lines") # Create conection to frontend @out plotdataC=[traceContour] @out plotlayoutC=layoutContour @@ -234,7 +256,14 @@ include("./julia_imzML_visual.jl") # Interface Plot 3d # Define the layout for the 3D plot layout3D=PlotlyBase.Layout( - title="3D Surface plot", + title=PlotlyBase.attr( + text="3D Surface plot", + font=PlotlyBase.attr( + family="Roboto, Lato, sans-serif", + size=18, + color="black" + ) + ), scene=attr( xaxis_title="X", yaxis_title="Y", @@ -386,69 +415,107 @@ include("./julia_imzML_visual.jl") # Use the new get_mz_slice with the centralized MSIData object println("get_mz_slice time:") slice = @time get_mz_slice(msi_data, Nmass, Tol) - fig = CairoMakie.Figure(size=(150, 250)) # Container - timestamp = string(time_ns()) - - if triqEnabled # If we have TrIQ - if triqProb < 0.8 || triqProb > 1 - msg = "Incorrect TrIQ values, please adjust accordingly and try again." - warning_msg = true - else - println("TrIQ time:") - sliceTriq = @time TrIQ(slice, colorLevel, triqProb) - if MFilterEnabled - sliceTriq = round.(UInt8, median_filter(sliceTriq)) - end - sliceTriq = reverse(sliceTriq, dims=2) - println("save_bitmap time:") - @time save_bitmap(joinpath("public", "TrIQ_$(text_nmass).bmp"), sliceTriq, ViridisPalette) + # Failsafe check for empty slice + if all(iszero, slice) + msg = "No intensity data found for m/z = $Nmass with tolerance = $Tol. The resulting image is black. Please consider using a larger tolerance." + warning_msg = true + + # Generate a black image but skip the colorbar that would crash + timestamp = string(time_ns()) + text_nmass = replace(string(Nmass), "." => "_") + sliceQuant = zeros(UInt8, size(slice)) + + if triqEnabled + @time save_bitmap(joinpath("public", "TrIQ_$(text_nmass).bmp"), sliceQuant, ViridisPalette) imgIntT = "/TrIQ_$(text_nmass).bmp?t=$(timestamp)" plotdataImgT, plotlayoutImgT, imgWidth, imgHeight = loadImgPlot(imgIntT) current_triq = "TrIQ_$(text_nmass).bmp" - msgtriq = "TrIQ image with the Nmass of $(replace(text_nmass, "_" => "."))" - - colorbar_path = joinpath("public", "colorbar_TrIQ_$(text_nmass).png") - println("generate_colorbar_image time:") - @time generate_colorbar_image(slice, colorLevel, colorbar_path, use_triq=true, triq_prob=triqProb) - colorbarT = "/colorbar_TrIQ_$(text_nmass).png?t=$(timestamp)" - current_col_triq = "colorbar_TrIQ_$(text_nmass).png" - + msgtriq = "TrIQ image with the Nmass of $(replace(text_nmass, "_" => ".")) (No data)" + colorbarT = "" # Clear colorbar + current_col_triq = "" triq_bmp = sort(filter(filename -> startswith(filename, "TrIQ_") && endswith(filename, ".bmp"), readdir("public")), lt=natural) - col_triq_png = sort(filter(filename -> startswith(filename, "colorbar_TrIQ_") && endswith(filename, ".png"), readdir("public")), lt=natural) - - fTime = time() - eTime = round(fTime - sTime, digits=3) - msg = "The TrIQ image has been created in $(eTime) seconds successfully inside the 'public' folder of the app" selectedTab = "tab1" + else + @time save_bitmap(joinpath("public", "MSI_$(text_nmass).bmp"), sliceQuant, ViridisPalette) + imgInt = "/MSI_$(text_nmass).bmp?t=$(timestamp)" + plotdataImg, plotlayoutImg, imgWidth, imgHeight = loadImgPlot(imgInt) + current_msi = "MSI_$(text_nmass).bmp" + msgimg = "Image with the Nmass of $(replace(text_nmass, "_" => ".")) (No data)" + colorbar = "" # Clear colorbar + current_col_msi = "" + msi_bmp = sort(filter(filename -> startswith(filename, "MSI_") && endswith(filename, ".bmp"), readdir("public")), lt=natural) + selectedTab = "tab0" end - else # If we don't use TrIQ - println("quantize_intensity time:") - sliceQuant = @time quantize_intensity(slice, colorLevel) - if MFilterEnabled - sliceQuant = round.(UInt8, median_filter(sliceQuant)) - end - sliceQuant = reverse(sliceQuant, dims=2) - println("save_bitmap time:") - @time save_bitmap(joinpath("public", "MSI_$(text_nmass).bmp"), sliceQuant, ViridisPalette) - - imgInt = "/MSI_$(text_nmass).bmp?t=$(timestamp)" - plotdataImg, plotlayoutImg, imgWidth, imgHeight = loadImgPlot(imgInt) - current_msi = "MSI_$(text_nmass).bmp" - msgimg = "Image with the Nmass of $(replace(text_nmass, "_" => "."))" - - colorbar_path = joinpath("public", "colorbar_MSI_$(text_nmass).png") - println("generate_colorbar_image time:") - @time generate_colorbar_image(slice, colorLevel, colorbar_path) - colorbar = "/colorbar_MSI_$(text_nmass).png?t=$(timestamp)" - current_col_msi = "colorbar_MSI_$(text_nmass).png" - - msi_bmp = sort(filter(filename -> startswith(filename, "MSI_") && endswith(filename, ".bmp"), readdir("public")), lt=natural) - col_msi_png = sort(filter(filename -> startswith(filename, "colorbar_MSI_") && endswith(filename, ".png"), readdir("public")), lt=natural) - selectedTab = "tab0" fTime = time() eTime = round(fTime - sTime, digits=3) - msg = "The image has been created in $(eTime) seconds successfully inside the 'public' folder of the app" + # The warning message is already set above + else + # Original path for when data is found + fig = CairoMakie.Figure(size=(150, 250)) # Container + timestamp = string(time_ns()) + + if triqEnabled # If we have TrIQ + if triqProb < 0.8 || triqProb > 1 + msg = "Incorrect TrIQ values, please adjust accordingly and try again." + warning_msg = true + else + println("TrIQ time:") + sliceTriq = @time TrIQ(slice, colorLevel, triqProb) + if MFilterEnabled + sliceTriq = round.(UInt8, median_filter(sliceTriq)) + end + + println("save_bitmap time:") + @time save_bitmap(joinpath("public", "TrIQ_$(text_nmass).bmp"), sliceTriq, ViridisPalette) + + imgIntT = "/TrIQ_$(text_nmass).bmp?t=$(timestamp)" + plotdataImgT, plotlayoutImgT, imgWidth, imgHeight = loadImgPlot(imgIntT) + current_triq = "TrIQ_$(text_nmass).bmp" + msgtriq = "TrIQ image with the Nmass of $(replace(text_nmass, "_" => "."))" + + colorbar_path = joinpath("public", "colorbar_TrIQ_$(text_nmass).png") + println("generate_colorbar_image time:") + @time generate_colorbar_image(slice, colorLevel, colorbar_path, use_triq=true, triq_prob=triqProb) + colorbarT = "/colorbar_TrIQ_$(text_nmass).png?t=$(timestamp)" + current_col_triq = "colorbar_TrIQ_$(text_nmass).png" + + triq_bmp = sort(filter(filename -> startswith(filename, "TrIQ_") && endswith(filename, ".bmp"), readdir("public")), lt=natural) + col_triq_png = sort(filter(filename -> startswith(filename, "colorbar_TrIQ_") && endswith(filename, ".png"), readdir("public")), lt=natural) + + fTime = time() + eTime = round(fTime - sTime, digits=3) + msg = "The TrIQ image has been created in $(eTime) seconds successfully inside the 'public' folder of the app" + selectedTab = "tab1" + end + else # If we don't use TrIQ + println("quantize_intensity time:") + sliceQuant = @time quantize_intensity(slice, colorLevel) + if MFilterEnabled + sliceQuant = round.(UInt8, median_filter(sliceQuant)) + end + + println("save_bitmap time:") + @time save_bitmap(joinpath("public", "MSI_$(text_nmass).bmp"), sliceQuant, ViridisPalette) + + imgInt = "/MSI_$(text_nmass).bmp?t=$(timestamp)" + plotdataImg, plotlayoutImg, imgWidth, imgHeight = loadImgPlot(imgInt) + current_msi = "MSI_$(text_nmass).bmp" + msgimg = "Image with the Nmass of $(replace(text_nmass, "_" => "."))" + + colorbar_path = joinpath("public", "colorbar_MSI_$(text_nmass).png") + println("generate_colorbar_image time:") + @time generate_colorbar_image(slice, colorLevel, colorbar_path) + colorbar = "/colorbar_MSI_$(text_nmass).png?t=$(timestamp)" + current_col_msi = "colorbar_MSI_$(text_nmass).png" + + msi_bmp = sort(filter(filename -> startswith(filename, "MSI_") && endswith(filename, ".bmp"), readdir("public")), lt=natural) + col_msi_png = sort(filter(filename -> startswith(filename, "colorbar_MSI_") && endswith(filename, ".png"), readdir("public")), lt=natural) + selectedTab = "tab0" + fTime = time() + eTime = round(fTime - sTime, digits=3) + msg = "The image has been created in $(eTime) seconds successfully inside the 'public' folder of the app" + end end catch e msg = "There was an error creating the image: $e" @@ -624,6 +691,7 @@ include("./julia_imzML_visual.jl") else traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}()) plotdataImg=[traceImg] + plotlayoutImg=layoutImg msgimg="" end end @@ -651,6 +719,7 @@ include("./julia_imzML_visual.jl") else traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}()) plotdataImg=[traceImg] + plotlayoutImg=layoutImg msgimg="" end end @@ -679,6 +748,7 @@ include("./julia_imzML_visual.jl") else traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}()) plotdataImgT=[traceImg] + plotlayoutImgT=layoutImg msgtriq="" end end @@ -706,6 +776,7 @@ include("./julia_imzML_visual.jl") else traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}()) plotdataImgT=[traceImg] + plotlayoutImgT=layoutImg msgtriq="" end end @@ -735,6 +806,7 @@ include("./julia_imzML_visual.jl") else traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}()) plotdataImgComp=[traceImg] + plotlayoutImgComp=layoutImg msgimgComp="" end end @@ -763,6 +835,7 @@ include("./julia_imzML_visual.jl") else traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}()) plotdataImgComp=[traceImg] + plotlayoutImgComp=layoutImg msgimgComp="" end end @@ -791,6 +864,7 @@ include("./julia_imzML_visual.jl") else traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}()) plotdataImgTComp=[traceImg] + plotlayoutImgTComp=layoutImg msgtriqComp="" end end @@ -819,6 +893,7 @@ include("./julia_imzML_visual.jl") else traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}()) plotdataImgTComp=[traceImg] + plotlayoutImgTComp=layoutImg msgtriqComp="" end end @@ -1026,9 +1101,20 @@ include("./julia_imzML_visual.jl") @onchange data_click begin if selectedTab == "tab1" || selectedTab == "tab0" # This is for the image heatmaps - cursor_data = data_click["cursor"] - x = Int32(round(cursor_data["x"])) - y = Int32(round(cursor_data["y"])) # y is negative in the UI + cursor_data = get(data_click, "cursor", nothing) + if cursor_data === nothing + return + end + + x_val = get(cursor_data, "x", nothing) + y_val = get(cursor_data, "y", nothing) + + if x_val === nothing || y_val === nothing + return # Do nothing if coordinates are not provided by the event + end + + x = Int32(round(x_val)) + y = Int32(round(y_val)) # y is negative in the UI # Update the reactive coordinates, which will trigger the crosshair update xCoord = clamp(x, 1, imgWidth) @@ -1107,3 +1193,40 @@ end # Register a new route and the page that will be loaded on access @page("/", "app.jl.html") end + +#= +function __init__() + @async begin + println("Pre-compiling functions at startup...") + + # Create a dummy MSIData object to be used for pre-compilation + dummy_source = ImzMLSource("dummy.ibd", Float32, Float32) + dummy_meta = MSI_src.SpectrumMetadata(0,0,"",MSI_src.UNKNOWN, MSI_src.SpectrumAsset(Float32,false,0,0,:mz), MSI_src.SpectrumAsset(Float32,false,0,0,:intensity)) + dummy_msi_data = MSIData(dummy_source, [dummy_meta], (1,1), zeros(Int,1,1), 0) + + # Pre-compile functions from btnSearch + try OpenMSIData("dummy.imzML") catch end + try precompute_analytics(dummy_msi_data) catch end + + # Pre-compile functions from mainProcess + try get_mz_slice(dummy_msi_data, 1.0, 1.0) catch end + try TrIQ(zeros(10,10), 256, 0.98) catch end + try quantize_intensity(zeros(10,10), 256) catch end + + dummy_bmp_path = joinpath("public", "dummy.bmp") + dummy_png_path = joinpath("public", "dummy.png") + try + save_bitmap(dummy_bmp_path, zeros(UInt8, 10, 10), ViridisPalette) + loadImgPlot("/dummy.bmp") + generate_colorbar_image(zeros(10,10), 256, dummy_png_path) + catch e + @warn "Pre-compilation step failed (this is expected if dummy files can't be created/read)" + finally + rm(dummy_bmp_path, force=true) + rm(dummy_png_path, force=true) + end + + println("Pre-compilation finished.") + end +end +=# diff --git a/app.jl.html b/app.jl.html index 88f0525..72693eb 100644 --- a/app.jl.html +++ b/app.jl.html @@ -1,5 +1,4 @@ -