Bitmap is now normalized in creation, which evenly distributes colors of the colorTable better, now IntQuant allows for coloLevel adjustments

This commit is contained in:
Pixelguy14 2025-01-30 00:40:01 -06:00
parent df0d82910b
commit fdd625d3e0
5 changed files with 41 additions and 31 deletions

View File

@ -22,7 +22,7 @@ A Graphical User Interface for MSI in Julia: https://github.com/CINVESTAV-LABI/j
``` ```
julia --project=. start_MSI_GUI.jl julia --project=. start_MSI_GUI.jl
``` ```
3. After the script has finished loading, it should open a page (http://127.0.0.1:1481/) in your browser with the web app running. 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.
Additional notes:<br> Additional notes:<br>
After the first boot initializes the packages in your computer, subsequent uses of the app should not take longer to load.<br> After the first boot initializes the packages in your computer, subsequent uses of the app should not take longer to load.<br>

31
app.jl
View File

@ -13,6 +13,7 @@ using Images
using LinearAlgebra using LinearAlgebra
using NativeFileDialog # Opens the file explorer depending on the OS using NativeFileDialog # Opens the file explorer depending on the OS
using StipplePlotly using StipplePlotly
using Printf
include("./julia_imzML_visual.jl") include("./julia_imzML_visual.jl")
@genietools @genietools
@ -121,7 +122,8 @@ function loadContourPlot(interfaceImg::String)
img=load(var) img=load(var)
img_gray=Gray.(img) img_gray=Gray.(img)
img_array=Array(img_gray) img_array=Array(img_gray)
elevation=Float32.(Array(img_gray)) ./ 255.0 # Normalize between 0 and 1 # parse matrix to int32 to closely resemble similarity to original bitmap
elevation=int32.(Array(img_gray))
# Smooth the image # Smooth the image
sigma=3.0 sigma=3.0
@ -487,7 +489,7 @@ end
text_nmass=replace(string(Nmass), "." => "_") text_nmass=replace(string(Nmass), "." => "_")
sTime=time() sTime=time()
#full_route=joinpath(file_route, file_name) #full_route=joinpath(file_route, file_name)
if isfile(full_route) && Nmass > 0 && Tol > 0 && Tol <=1 if isfile(full_route) && Nmass > 0 && Tol > 0 && Tol <=1 && colorLevel > 1 && colorLevel < 257
msg="File exists, Nmass=$(Nmass) Tol=$(Tol). Loading file will begin, please be patient." msg="File exists, Nmass=$(Nmass) Tol=$(Tol). Loading file will begin, please be patient."
try try
spectra=LoadImzml(full_route) spectra=LoadImzml(full_route)
@ -497,12 +499,12 @@ end
# Append a query string to force the image to refresh # Append a query string to force the image to refresh
timestamp=string(time_ns()) timestamp=string(time_ns())
if triqEnabled # If we have TrIQ if triqEnabled # If we have TrIQ
if colorLevel < 2 || colorLevel > 256 || triqProb < 0.8 || triqProb > 1 if triqProb < 0.8 || triqProb > 1
msg="Incorrect TrIQ values, please adjust accordingly and try again." msg="Incorrect TrIQ values, please adjust accordingly and try again."
warning_msg=true warning_msg=true
else else
image_path=joinpath("./public", "TrIQ_$(text_nmass).bmp") image_path=joinpath("./public", "TrIQ_$(text_nmass).bmp")
sliceTriq=TrIQ(slice, Int(colorLevel), triqProb) sliceTriq=TrIQ(slice, colorLevel, triqProb)
#println("slice raw: $(typeof(slice))") #println("slice raw: $(typeof(slice))")
#println("TriQ matrix: $(typeof(sliceTriq))") #println("TriQ matrix: $(typeof(sliceTriq))")
sliceTriq=reverse(sliceTriq, dims=2) sliceTriq=reverse(sliceTriq, dims=2)
@ -515,8 +517,19 @@ end
current_triq="TrIQ_$(text_nmass).bmp" current_triq="TrIQ_$(text_nmass).bmp"
msgtriq="TrIQ image with the Nmass of $(replace(text_nmass, "_" => "."))" msgtriq="TrIQ image with the Nmass of $(replace(text_nmass, "_" => "."))"
# Create colorbar # Create colorbar
ticks=round.(range(0, (stop=maximum(sliceTriq)*triqProb), length=15), sigdigits=3) #ticks=round.(range(0, (stop=maximum(slice)*triqProb), length=10), sigdigits=3)
Colorbar(fig[1, 1], colormap=cgrad(:viridis, colorLevel, categorical=true), limits=(0, maximum(sliceTriq)*triqProb),ticks=ticks, label="Intensity", size = 25) ticks=range(0, (stop=maximum(slice)*triqProb), length=5)
"""
function log_tick_formatter(values)
return map(v -> "10" * Makie.UnicodeFun.to_superscript(round(Int64, v)), values)
end
"""
println("ticks: $(typeof(ticks))")
#Colorbar(fig[1, 1], colormap=cgrad(:viridis, colorLevel, categorical=true), limits=(0, maximum(slice)*triqProb),ticks=ticks,tickformat=log_tick_formatter, label="Intensity", size = 25)
Colorbar(fig[1, 1], colormap=cgrad(:viridis, colorLevel, categorical=true), limits=(0, maximum(slice)*triqProb),ticks=ticks, label="Intensity", size = 25)
println("Colorbar: $(typeof(fig))")
save("public/colorbar_TrIQ_$(text_nmass).png", fig) save("public/colorbar_TrIQ_$(text_nmass).png", fig)
colorbarT="/colorbar_TrIQ_$(text_nmass).png?t=$(timestamp)" colorbarT="/colorbar_TrIQ_$(text_nmass).png?t=$(timestamp)"
# Get current colorbar # Get current colorbar
@ -534,7 +547,7 @@ end
else # If we don't use TrIQ else # If we don't use TrIQ
image_path=joinpath("./public", "MSI_$(text_nmass).bmp") image_path=joinpath("./public", "MSI_$(text_nmass).bmp")
##sliceQuant=IntQuant(slice) ##sliceQuant=IntQuant(slice)
sliceQuant=IntQuantCl(slice,colorLevel) sliceQuant=IntQuantCl(slice,Int(colorLevel-1))
#println("slice raw: $(typeof(slice))") #println("slice raw: $(typeof(slice))")
#println("slice in intQuant: $(typeof(sliceQuant))") #println("slice in intQuant: $(typeof(sliceQuant))")
sliceQuant=reverse(sliceQuant, dims=2) sliceQuant=reverse(sliceQuant, dims=2)
@ -548,8 +561,8 @@ end
msgimg="Image with the Nmass of $(replace(text_nmass, "_" => "."))" msgimg="Image with the Nmass of $(replace(text_nmass, "_" => "."))"
# Create colorbar # Create colorbar
#ticks=round.(range(0, stop=maximum(slice), length=10), sigdigits=3) #ticks=round.(range(0, stop=maximum(slice), length=10), sigdigits=3)
ticks=round.(range(0, stop=maximum(sliceQuant), length=15), sigdigits=3) ticks=range(0, stop=maximum(slice), length=5)
Colorbar(fig[1, 1], colormap=cgrad(:viridis, colorLevel, categorical=true), limits=(0, maximum(sliceQuant)),ticks=ticks, label="Intensity", size = 25) Colorbar(fig[1, 1], colormap=cgrad(:viridis, colorLevel, categorical=true), limits=(0, maximum(slice)),ticks=ticks, label="Intensity", size = 25)
save("public/colorbar_MSI_$(text_nmass).png", fig) save("public/colorbar_MSI_$(text_nmass).png", fig)
colorbar="/colorbar_MSI_$(text_nmass).png?t=$(timestamp)" colorbar="/colorbar_MSI_$(text_nmass).png?t=$(timestamp)"
# Get current colorbar # Get current colorbar

View File

@ -60,18 +60,16 @@
<div class="st-col col-4 col-sm q-ma-sm"> <div class="st-col col-4 col-sm q-ma-sm">
<q-input standout="custom-standout" id="textNmass" step="0.01" v-model="Nmass" <q-input standout="custom-standout" id="textNmass" step="0.01" v-model="Nmass"
label="Mass-to-charge ratio of interest" type="number" label="Mass-to-charge ratio of interest" type="number"
:rules="[ val => !!val || '* Required', val => val >= 0.0 || 'Need positive mass values',]"></q-input> :rules="[ val => !!val || '* Required', val => val >= 0.0 || 'Need positive mass values']"></q-input>
</div> </div>
<div class="st-col col-4 col-sm q-ma-sm"> <div class="st-col col-4 col-sm q-ma-sm">
<q-input standout="custom-standout" id="textTol" step="0.05" v-model="Tol" <q-input standout="custom-standout" id="textTol" step="0.005" v-model="Tol"
label="Mass-to-charge ratio tolerance" type="number" label="Mass-to-charge ratio tolerance" type="number"
:rules="[val => !!val || '* Required', val => val >= 0.0 &amp;&amp; val <= 1.0 || 'Needs to be in range between 0 and 1',]"></q-input> :rules="[val => !!val || '* Required', val => val >= 0.0 &amp;&amp; val <= 1.0 || 'Needs to be in range between 0 and 1']"></q-input>
</div> </div>
<div class="st-col col-4 col-sm q-ma-sm"> <div class="st-col col-4 col-sm q-ma-sm">
<q-input standout="custom-standout" id="textcolorLevel" step="1" v-model="colorLevel" <q-input standout="custom-standout" id="textcolorLevel" step="1" v-model="colorLevel"
label="Color level" type="number" :rules="[ label="Color level" type="number" :rules="[ val => !!val || '* Required', val => val >= 2 &amp;&amp; val <= 256 || 'Needs to be in range between 2 and 256']"></q-input>
val => triqEnabled ? ( '* Required', val >= 4 &amp;&amp; val <= 256 || 'Needs to be in range between 2 and 256') : true
]"></q-input>
</div> </div>
</div> </div>
<div class="row"> <div class="row">

View File

@ -15,14 +15,16 @@ end
# SaveBitmap is also originally a function of the mzML imzML library in julia, # SaveBitmap is also originally a function of the mzML imzML library in julia,
# This function dinamically adjust the color palete adjusting to the ammount of colors # This function dinamically adjust the color palete adjusting to the ammount of colors
# available in pixmap # available in pixmap
function SaveBitmapCl( name, function SaveBitmapCl( name, pixMap::Array{UInt8,2}, colorTable::Array{UInt32,1} )
pixMap::Array{UInt8,2},
colorTable::Array{UInt32,1} )
# Get image dimensions # Get image dimensions
dim = size( pixMap ) dim = size( pixMap )
if length( dim ) != 2 if length( dim ) != 2
return 0 return 0
end end
# Normalize pixel values to get a more accurate reading of the image
min_val = minimum(pixMap)
max_val = maximum(pixMap)
pixMap = round.(UInt8, 255 * (pixMap .- min_val) ./ (max_val - min_val))
# Compute row padding # Compute row padding
padding = ( 4 - dim[1] & 0x3 ) & 0x3 padding = ( 4 - dim[1] & 0x3 ) & 0x3
# Compute file dimensions. Header = 14 + 40 + ( 256 * 4 ) = 1078 # Compute file dimensions. Header = 14 + 40 + ( 256 * 4 ) = 1078
@ -36,17 +38,12 @@ function SaveBitmapCl( name,
# Save info header # Save info header
write( stream, UInt32[ 40, dim[1], dim[2], 0x80001, 0 ] ) write( stream, UInt32[ 40, dim[1], dim[2], 0x80001, 0 ] )
write( stream, UInt32[ imgBytes, 0, 0, 256, 0 ] ) write( stream, UInt32[ imgBytes, 0, 0, 256, 0 ] )
# Get unique colors displayed in pixMap # Save color table
unique_colors = unique(vec(pixMap)) write( stream, colorTable )
n_colors = length(unique_colors) if length( colorTable ) < 256
# Color levels can't surpass 256 fixTable = zeros( UInt32, 256 - length( colorTable ) )
levels = min(n_colors, 256) write( stream, fixTable )
extendedColorTable = zeros(UInt32, 256)
for i in 1:256
idx = ceil(Int, i * levels / 256)
extendedColorTable[i] = colorTable[idx]
end end
write(stream, extendedColorTable)
# Save image pixels # Save image pixels
if padding == 0 if padding == 0
for i = 1:dim[2] for i = 1:dim[2]

View File

@ -1,4 +1,5 @@
using Pkg using Pkg
sTime=time()
Pkg.activate(".") Pkg.activate(".")
Pkg.instantiate() Pkg.instantiate()
Pkg.gc() Pkg.gc()
@ -31,6 +32,8 @@ Genie.loadapp()
# Start the Genie server # Start the Genie server
@async begin @async begin
up(host="127.0.0.1", port=1481) up(host="127.0.0.1", port=1481)
eTime=round(time()-sTime,digits=3)
println("Julia MSI GUI took $(eTime) seconds booting")
url = "http://127.0.0.1:1481" url = "http://127.0.0.1:1481"
# Open the URL in the default web browser based on the OS # Open the URL in the default web browser based on the OS
if Sys.isapple() if Sys.isapple()
@ -43,5 +46,4 @@ Genie.loadapp()
@async run(`Start-Process $url`) # For Windows @async run(`Start-Process $url`) # For Windows
end end
end end
wait() wait()