Merge pull request #7 from Pixelguy14/Experimental_ColorLevels
Experimental color levels done
This commit is contained in:
commit
09d680db87
@ -22,7 +22,7 @@ A Graphical User Interface for MSI in Julia: https://github.com/CINVESTAV-LABI/j
|
||||
```
|
||||
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>
|
||||
After the first boot initializes the packages in your computer, subsequent uses of the app should not take longer to load.<br>
|
||||
|
||||
121
app.jl
121
app.jl
@ -13,6 +13,7 @@ using Images
|
||||
using LinearAlgebra
|
||||
using NativeFileDialog # Opens the file explorer depending on the OS
|
||||
using StipplePlotly
|
||||
include("./julia_imzML_visual.jl")
|
||||
@genietools
|
||||
|
||||
# == Code import ==
|
||||
@ -54,13 +55,13 @@ function loadImgPlot(interfaceImg::String)
|
||||
cleaned_img=lstrip(cleaned_img, '/')
|
||||
var=joinpath("./public", cleaned_img)
|
||||
img=load(var)
|
||||
println("type of img: $(typeof(img))")
|
||||
#println("type of img: $(typeof(img))")
|
||||
# Convert to grayscale
|
||||
img_gray=Gray.(img)
|
||||
img_array=Array(img_gray)
|
||||
#println(typeof(img_array))
|
||||
elevation=Float32.(Array(img_array)) ./ 255.0
|
||||
println("type of elevation: $(typeof(elevation))")
|
||||
#println("type of elevation: $(typeof(elevation))")
|
||||
# Get the X, Y coordinates of the image
|
||||
height, width=size(img_array)
|
||||
#println("height: $(height), width: $(width)")
|
||||
@ -120,7 +121,8 @@ function loadContourPlot(interfaceImg::String)
|
||||
img=load(var)
|
||||
img_gray=Gray.(img)
|
||||
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=Float32.(Array(img_array))./ 255.0 # Normalize between 0 and 1
|
||||
|
||||
# Smooth the image
|
||||
sigma=3.0
|
||||
@ -171,7 +173,7 @@ function loadSurfacePlot(interfaceImg::String)
|
||||
img_gray=Gray.(img) # Convert to grayscale
|
||||
#println("Grayscale image type:", typeof(img_gray))
|
||||
img_array=Array(img_gray)
|
||||
elevation=Float32.(Array(img_gray)) ./ 255.0 # Normalize between 0 and 1
|
||||
elevation=Float32.(Array(img_array)) ./ 255.0 # Normalize between 0 and 1
|
||||
#println("Elevation size:", size(elevation))
|
||||
# Smooth the image
|
||||
sigma=3.0
|
||||
@ -203,10 +205,12 @@ function loadSurfacePlot(interfaceImg::String)
|
||||
aspectratio=aspect_ratio
|
||||
)
|
||||
)
|
||||
# Transpose the elevation_smoothed array if Y axis is longer than X axis to fix chopping
|
||||
elevation_smoothed=transpose(elevation_smoothed)
|
||||
if size(elevation_smoothed, 1) < size(elevation_smoothed, 2)
|
||||
# Transpose the elevation_smoothed array if Y axis is longer than X axis to fix chopping
|
||||
elevation_smoothed=transpose(elevation_smoothed)
|
||||
Y=-Y
|
||||
else
|
||||
X=-X
|
||||
end
|
||||
|
||||
trace3D=PlotlyBase.surface(
|
||||
@ -243,8 +247,42 @@ function crossLinesPlot(x, y, maxwidth, maxheight)
|
||||
return trace1, trace2
|
||||
end
|
||||
|
||||
function log_tick_formatter(values::Vector{Float64})
|
||||
#println("values: $(values) + type: $(typeof(values))")
|
||||
# Initialize exponents dictionary
|
||||
exponents=zeros(Int, length(values))
|
||||
formValues=zeros(Float64, length(values))
|
||||
for i in 1:length(values)
|
||||
value = values[i]
|
||||
#println("value: $(value)")
|
||||
if value >= 1000 # positive formatting for notation
|
||||
while value >= 1000
|
||||
value /= 10
|
||||
exponents[i] += 1
|
||||
end
|
||||
#println("value: $(value) x10^$(exponents[i])")
|
||||
elseif value > 0 && value < 1 # negative formatting for notation
|
||||
while value < 1
|
||||
value *= 10
|
||||
exponents[i] -= 1
|
||||
end
|
||||
#values[i] = round(values[i], digits=2)
|
||||
#println("value: $(value) x10^$(exponents[i])")
|
||||
end
|
||||
#formatted_values[i] = values[i] != 0 ? "$(round(values[i], digits=2))x10" * Makie.UnicodeFun.to_superscript(exponents[i]) : "0"
|
||||
formValues[i]=value
|
||||
end
|
||||
#return formatted_values
|
||||
#return map(v -> values * "x10" * Makie.UnicodeFun.to_superscript(round(Int64, v)), exponents)
|
||||
#println("formated values: $(formValues) + type: $(typeof(formValues))")
|
||||
#println("exponents: $(exponents) + type: $(typeof(exponents))")
|
||||
#return map((v, e) -> "$(round(v, digits=2))x10" * Makie.UnicodeFun.to_superscript(e), formValues, exponents)
|
||||
return map((v, e) -> e == 0 ? "$(round(v, sigdigits=2))" : "$(round(v, sigdigits=2))x10" * Makie.UnicodeFun.to_superscript(e), formValues, exponents)
|
||||
|
||||
end
|
||||
|
||||
# == Reactive code ==
|
||||
# reactive code to make the UI interactive
|
||||
# Reactive code to make the UI interactive
|
||||
@app begin
|
||||
# == Reactive variables ==
|
||||
# reactive variables exist in both the Julia backend and the browser with two-way synchronization
|
||||
@ -476,6 +514,7 @@ end
|
||||
end
|
||||
|
||||
@onbutton mainProcess begin
|
||||
#@onchange Nmass begin
|
||||
progress=true # Start progress button animation
|
||||
btnStartDisable=true # We disable the button to avoid multiple requests
|
||||
btnPlotDisable=true
|
||||
@ -483,44 +522,46 @@ end
|
||||
text_nmass=replace(string(Nmass), "." => "_")
|
||||
sTime=time()
|
||||
#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."
|
||||
try
|
||||
spectra=LoadImzml(full_route)
|
||||
msg="File loaded. Creating Spectra with the specific mass and tolerance, please be patient."
|
||||
slice=GetSlice(spectra, Nmass, Tol)
|
||||
fig=CairoMakie.Figure(size=(140, 440)) # Container
|
||||
#slice=GetSlice(spectra, Nmass, Tol)
|
||||
slice=GetMzSliceJl(spectra,Nmass,Tol)
|
||||
fig=CairoMakie.Figure(size=(150, 250)) # Container
|
||||
# Append a query string to force the image to refresh
|
||||
timestamp=string(time_ns())
|
||||
if triqEnabled # If we have TrIQ
|
||||
if colorLevel < 4 || colorLevel > 256 || triqProb < 0.8 || triqProb > 1
|
||||
if triqProb < 0.8 || triqProb > 1
|
||||
msg="Incorrect TrIQ values, please adjust accordingly and try again."
|
||||
warning_msg=true
|
||||
else
|
||||
image_path=joinpath("./public", "TrIQ_$(text_nmass).bmp")
|
||||
SaveBitmap(joinpath("public", "TrIQ_$(text_nmass).bmp"),TrIQ(slice, Int(colorLevel), triqProb),ViridisPalette)
|
||||
# Flip te image vertically then save it again
|
||||
img=load(image_path)
|
||||
if size(img, 1) > size(img, 2) # fix to taller images
|
||||
img=reverse(permutedims(img, (2, 1)), dims=1)
|
||||
end
|
||||
flipped_img=reverse(img, dims=1)
|
||||
imgWidth=size(flipped_img, 2)
|
||||
imgHeight=size(flipped_img, 1)
|
||||
save(image_path, flipped_img)
|
||||
sliceTriq=TrIQ(slice, colorLevel, triqProb)
|
||||
#println("slice raw: $(typeof(slice))")
|
||||
#println("TriQ matrix: $(typeof(sliceTriq))")
|
||||
sliceTriq=reverse(sliceTriq, dims=2)
|
||||
##SaveBitmap(joinpath("public", "TrIQ_$(text_nmass).bmp"),sliceTriq,ViridisPalette)
|
||||
SaveBitmapCl(joinpath("public", "TrIQ_$(text_nmass).bmp"),sliceTriq,ViridisPalette)
|
||||
# Use timestamp to refresh image interface container
|
||||
imgIntT="/TrIQ_$(text_nmass).bmp?t=$(timestamp)"
|
||||
plotdataImgT, plotlayoutImgT, imgWidth, imgHeight=loadImgPlot(imgIntT)
|
||||
# Get current image
|
||||
current_triq="TrIQ_$(text_nmass).bmp"
|
||||
msgtriq="TrIQ image with the Nmass of $(replace(text_nmass, "_" => "."))"
|
||||
# Create colorbar
|
||||
#ticks=round.(range(0, stop=maximum(TrIQ(slice, Int(colorLevel), triqProb)), length=10), sigdigits=3)
|
||||
#println("ticks: $(round.(range(0, stop=maximum(TrIQ(slice, Int(colorLevel), triqProb)), length=10), sigdigits=3))")
|
||||
ticks=round.(range(0, (stop=maximum(slice)*triqProb), length=15), sigdigits=3)
|
||||
#println("ticks 2: $(round.(range(0, (stop=maximum(slice)*triqProb), length=15), sigdigits=3))")
|
||||
#Colorbar(fig[1, 1], colormap=rgb_ViridisPalette, limits=(0, maximum(TrIQ(slice, Int(colorLevel), triqProb))),ticks=ticks, label="Intensity")
|
||||
Colorbar(fig[1, 1], colormap=cgrad(:viridis, colorLevel, categorical=true), limits=(0, maximum(slice)*triqProb),ticks=ticks, label="Intensity", size = 25)
|
||||
# Create colorbar
|
||||
bound = julia_mzML_imzML.GetOutlierThres(slice, triqProb)
|
||||
levels = range(bound[1],stop=bound[2], length=8)
|
||||
#println("range of the levels 1: $(levels)")
|
||||
levels = vcat(levels, 2*levels[end]-levels[end-1])
|
||||
#println("range of the levels 2: $(levels) + $(length(levels))")
|
||||
#ticks=round.(range(0, (stop=maximum(slice)*triqProb), length=10), sigdigits=3)
|
||||
#ticks=range(0, (stop=maximum(slice)*triqProb), length=5)
|
||||
#println("ticks: $(typeof(ticks))")
|
||||
Colorbar(fig[1, 1], colormap=cgrad(:viridis, colorLevel, categorical=true), limits=(0, bound[2]),ticks=levels,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)
|
||||
colorbarT="/colorbar_TrIQ_$(text_nmass).png?t=$(timestamp)"
|
||||
# Get current colorbar
|
||||
@ -537,16 +578,13 @@ end
|
||||
end
|
||||
else # If we don't use TrIQ
|
||||
image_path=joinpath("./public", "MSI_$(text_nmass).bmp")
|
||||
SaveBitmap(joinpath("public", "MSI_$(text_nmass).bmp"),IntQuant(slice),ViridisPalette)
|
||||
# Flip te image vertically then save it again
|
||||
img=load(image_path)
|
||||
if size(img, 1) > size(img, 2) # fix to taller images
|
||||
img=reverse(permutedims(img, (2, 1)), dims=1)
|
||||
end
|
||||
flipped_img=reverse(img, dims=1)
|
||||
imgWidth=size(flipped_img, 2)
|
||||
imgHeight=size(flipped_img, 1)
|
||||
save(image_path, flipped_img)
|
||||
##sliceQuant=IntQuant(slice)
|
||||
sliceQuant=IntQuantCl(slice,Int(colorLevel-1))
|
||||
#println("slice raw: $(typeof(slice))")
|
||||
#println("slice in intQuant: $(typeof(sliceQuant))")
|
||||
sliceQuant=reverse(sliceQuant, dims=2)
|
||||
##SaveBitmap(joinpath("public", "MSI_$(text_nmass).bmp"),sliceQuant,ViridisPalette)
|
||||
SaveBitmapCl(joinpath("public", "MSI_$(text_nmass).bmp"),sliceQuant,ViridisPalette)
|
||||
# Use timestamp to refresh image interface container
|
||||
imgInt="/MSI_$(text_nmass).bmp?t=$(timestamp)"
|
||||
plotdataImg, plotlayoutImg, imgWidth, imgHeight=loadImgPlot(imgInt)
|
||||
@ -554,10 +592,11 @@ end
|
||||
current_msi="MSI_$(text_nmass).bmp"
|
||||
msgimg="Image with the Nmass of $(replace(text_nmass, "_" => "."))"
|
||||
# Create colorbar
|
||||
levels=range(0,maximum(slice),length=8)
|
||||
#ticks=round.(range(0, stop=maximum(slice), length=10), sigdigits=3)
|
||||
ticks=round.(range(0, stop=maximum(slice), length=15), sigdigits=3)
|
||||
#Colorbar(fig[1, 1], colormap=rgb_ViridisPalette, limits=(0, maximum(slice)),ticks=ticks, label="Intensity")
|
||||
Colorbar(fig[1, 1], colormap=cgrad(:viridis, 256, categorical=true), limits=(0, maximum(slice)),ticks=ticks, label="Intensity", size = 25)
|
||||
#ticks=range(0, stop=maximum(slice), length=5)
|
||||
#Colorbar(fig[1, 1], colormap=cgrad(:viridis, colorLevel, categorical=true), limits=(0, maximum(slice)),ticks=ticks, label="Intensity", size = 25)
|
||||
Colorbar(fig[1, 1], colormap=cgrad(:viridis, colorLevel, categorical=true), limits=(0, maximum(slice)),ticks=levels,tickformat=log_tick_formatter, label="Intensity", size = 25)
|
||||
save("public/colorbar_MSI_$(text_nmass).png", fig)
|
||||
colorbar="/colorbar_MSI_$(text_nmass).png?t=$(timestamp)"
|
||||
# Get current colorbar
|
||||
|
||||
26
app.jl.html
26
app.jl.html
@ -57,21 +57,27 @@
|
||||
<p id="lblFullRoute">full route: {{full_route}}</p>
|
||||
<!-- Variable Manipulation -->
|
||||
<div class="row">
|
||||
<div class="st-col col-12 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"
|
||||
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 class="st-col col-12 col-sm q-ma-sm">
|
||||
<q-input standout="custom-standout" id="textTol" step="0.05" v-model="Tol"
|
||||
<div class="st-col col-4 col-sm q-ma-sm">
|
||||
<q-input standout="custom-standout" id="textTol" step="0.005" v-model="Tol"
|
||||
label="Mass-to-charge ratio tolerance" type="number"
|
||||
:rules="[val => !!val || '* Required', val => val >= 0.0 && val <= 1.0 || 'Needs to be in range between 0 and 1',]"></q-input>
|
||||
:rules="[val => !!val || '* Required', val => val >= 0.0 && val <= 1.0 || 'Needs to be in range between 0 and 1']"></q-input>
|
||||
</div>
|
||||
<div class="st-col col-4 col-sm q-ma-sm">
|
||||
<q-input standout="custom-standout" id="textcolorLevel" step="1" v-model="colorLevel"
|
||||
label="Color level" type="number" :rules="[ val => !!val || '* Required', val => val >= 2 && val <= 256 || 'Needs to be in range between 2 and 256']"></q-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<!-- Triq Variable Manipulation -->
|
||||
<!-- Triq Variable Manipulation and filters-->
|
||||
<div class="col-6">
|
||||
<div class="st-col col-6 col-sm q-ma-sm">
|
||||
<q-toggle id="btnEnableMFilter" v-on:click="MFilterEnabled" v-model="MFilterEnabled" color="green"
|
||||
label="Add Median Filter"></q-toggle>
|
||||
<q-toggle id="btnEnableTriq" v-on:click="triqEnabled" v-model="triqEnabled" color="blue"
|
||||
label="Add Threshold Intensity Quantization (TrIQ)"></q-toggle>
|
||||
</div>
|
||||
@ -82,12 +88,8 @@
|
||||
val => triqEnabled ? ( '* Required', val >= 0.8 && val <= 1 || 'Needs to be in range between 0.8 and 1') : true
|
||||
]" :readonly="!triqEnabled" :disable="!triqEnabled"></q-input>
|
||||
</div>
|
||||
<div class="st-col col-4 col-sm-4 q-ma-sm">
|
||||
<q-input standout="custom-standout" id="textcolorLevel" step="1" v-model="colorLevel"
|
||||
label="TrIQ color levels" type="number" :rules="[
|
||||
val => triqEnabled ? ( '* Required', val >= 4 && val <= 256 || 'Needs to be in range between 2 and 256') : true
|
||||
]" :readonly="!triqEnabled" :disable="!triqEnabled"></q-input>
|
||||
</div>
|
||||
<!--<div class="st-col col-4 col-sm-4 q-ma-sm">
|
||||
</div>-->
|
||||
</div>
|
||||
</div>
|
||||
<!-- Spectra Plot Manipulation -->
|
||||
|
||||
80
julia_imzML_visual.jl
Normal file
80
julia_imzML_visual.jl
Normal file
@ -0,0 +1,80 @@
|
||||
# IntQuantCl is originally a function of julia mzMl imzML with the adition
|
||||
# of altering the scale of the colors according to colorlevel.
|
||||
function IntQuantCl( slice , colorLevel)
|
||||
# Compute scale factor for amplitude discretization
|
||||
lower = minimum( slice )
|
||||
scale = colorLevel / maximum( slice )
|
||||
dim = size( slice )
|
||||
image = zeros( UInt8, dim[1], dim[2] )
|
||||
for i in 1:length( slice )
|
||||
image[i] = convert( UInt8, floor( slice[i] * scale + 0.5 ) )
|
||||
end
|
||||
return image
|
||||
end
|
||||
|
||||
# SaveBitmap originally a function of the mzML imzML library in julia,
|
||||
# This function dinamically adjust the color palete adjusting to the ammount of colors
|
||||
# available in pixmap
|
||||
function SaveBitmapCl( name, pixMap::Array{UInt8,2}, colorTable::Array{UInt32,1} )
|
||||
# Get image dimensions
|
||||
dim = size( pixMap )
|
||||
if length( dim ) != 2
|
||||
return 0
|
||||
end
|
||||
# Normalize pixel values to get a more accurate reading of the image
|
||||
minVal = minimum(pixMap)
|
||||
maxVal = maximum(pixMap)
|
||||
pixMap = round.(UInt8, 255 * (pixMap .- minVal) ./ (maxVal - minVal))
|
||||
# Compute row padding
|
||||
padding = ( 4 - dim[1] & 0x3 ) & 0x3
|
||||
# Compute file dimensions. Header = 14 + 40 + ( 256 * 4 ) = 1078
|
||||
offset = 1078
|
||||
imgBytes = dim[2] * ( dim[1] + padding )
|
||||
# Create file
|
||||
stream = open( name, "w" )
|
||||
# Save file header
|
||||
write( stream, UInt16( 0x4D42 ) )
|
||||
write( stream, UInt32[ offset + imgBytes, 0 , offset ] )
|
||||
# Save info header
|
||||
write( stream, UInt32[ 40, dim[1], dim[2], 0x80001, 0 ] )
|
||||
write( stream, UInt32[ imgBytes, 0, 0, 256, 0 ] )
|
||||
# Save color table
|
||||
write( stream, colorTable )
|
||||
if length( colorTable ) < 256
|
||||
fixTable = zeros( UInt32, 256 - length( colorTable ) )
|
||||
write( stream, fixTable )
|
||||
end
|
||||
# Save image pixels
|
||||
if padding == 0
|
||||
for i = 1:dim[2]
|
||||
write( stream, pixMap[:,i] )
|
||||
end
|
||||
else
|
||||
zeroPad = zeros( UInt8, padding )
|
||||
for i in 1:dim[2]
|
||||
write( stream, pixMap[:,i] )
|
||||
write( stream, zeroPad )
|
||||
end
|
||||
end
|
||||
# Close file
|
||||
close( stream )
|
||||
end
|
||||
|
||||
# SaveBitmap originally a function of the mzML imzML library in julia,
|
||||
# now has an adjustment for NaN values in case they exist to mantain data integrity
|
||||
function GetMzSliceJl(imzML, mass, tolerance)
|
||||
# Alloc space for slice
|
||||
width = maximum(imzML[1, :])
|
||||
height = maximum(imzML[2, :])
|
||||
image = fill(0.0, width, height)
|
||||
|
||||
for i in 1:size(imzML)[2]
|
||||
index = julia_mzML_imzML.FindMass(imzML[3, i], mass, tolerance)
|
||||
if index != 0
|
||||
image[imzML[1, i], imzML[2, i]] = imzML[4, i][index]
|
||||
end
|
||||
end
|
||||
# Adjustment for NaN values with 0
|
||||
replace!(image, NaN => 0.0)
|
||||
return image
|
||||
end
|
||||
@ -1,8 +1,10 @@
|
||||
using Pkg
|
||||
sTime=time()
|
||||
Pkg.activate(".")
|
||||
Pkg.instantiate()
|
||||
Pkg.gc()
|
||||
|
||||
"""
|
||||
packages = [
|
||||
"GenieFramework", "Libz", "PlotlyBase", "CairoMakie", "Colors",
|
||||
"Statistics", "NaturalSort", "Genie",
|
||||
@ -20,6 +22,7 @@ end
|
||||
if !("julia_mzML_imzML" in keys(Pkg.dependencies()))
|
||||
Pkg.add(url="https://github.com/CINVESTAV-LABI/julia_mzML_imzML")
|
||||
end
|
||||
"""
|
||||
|
||||
using Genie
|
||||
|
||||
@ -29,6 +32,8 @@ Genie.loadapp()
|
||||
# Start the Genie server
|
||||
@async begin
|
||||
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"
|
||||
# Open the URL in the default web browser based on the OS
|
||||
if Sys.isapple()
|
||||
@ -41,5 +46,4 @@ Genie.loadapp()
|
||||
@async run(`Start-Process $url`) # For Windows
|
||||
end
|
||||
end
|
||||
|
||||
wait()
|
||||
Loading…
x
Reference in New Issue
Block a user