Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Phase Contrast Imaging #9

Open
drswgw opened this issue Mar 2, 2022 · 141 comments
Open

Phase Contrast Imaging #9

drswgw opened this issue Mar 2, 2022 · 141 comments

Comments

@drswgw
Copy link

drswgw commented Mar 2, 2022

Hi Korbinian,
Moving the dialog here....the clear SWI portion of things is all resolved. I've moved the newer ROMEO & Laplacian unwrapping stuff here, so you can close the SWI part at your leisure. Right now your answer to the question below is still in the SWI part.

@drswgw
Copy link
Author

drswgw commented Mar 2, 2022

Hi Korbinian,
OK CLEARSWI is working, using MRItools 3.3.1 and being careful to use single quotes 'a' not "a". ClearSWI turns out to be no good for my application: asynchronous phase based encoding of motions/velocities etc. Currently SWI doesn't output the unwrapped phase data, just a magnitude image and the MIP, which for me the MIP is giving a different number of slices than the input data and the magnitude output just isn't my cup of tea. Some of the input slices come in blank, so that may be why the MIP is missing some slices, but it messes up the masking etc. to change the number of slices.
So on to Julia to try and implement the Laplacian unwrapping.This is my version of Julia already installed, thought I would upgrade to version 1.7.2 to match the current version of the manual from Julia. Which version of Julia does ROMEO and clearSWI use?
image

@drswgw
Copy link
Author

drswgw commented Mar 2, 2022

Hi Korbinian,
Baby steps. Julia up and running, the import for "MriResearchTools" and "FFTW" went swimmingly. What about masking? Probably need to set a mask and call it from Julia, how to do that?
image

mre_intrnsep2d_mre_v07_10phs_PRS_150Hz_Pat3_33_FCOFF_A1

@drswgw
Copy link
Author

drswgw commented Mar 2, 2022

This movie (it's asynchronous, but there are beat frequencies) gives a reasonably good idea of the data, the phase values encode the motion(s). Need unwrapping that preserves the original phase values, just adds/subtracts 2 pi, 4 pi etc. as needed. Once that part is done can move on to the timing, gradients etc.

@korbinian90
Copy link
Owner

Unwrapping

As laplacian unwrapping produces arbitrary additions/subtractions and not +-2pi, best to use romeo.
In the julia REPL, you can write ?romeo to get function hints on how to use it.
For your data, the best option should be to use it on the 4D dataset (timepoints in the 4th dimension) like this:

using MriResearchTools
phase4D = readphase("path-to-phase/phase.nii")
unwrapped = romeo(phase4D; TEs=ones(size(phase,4)))
savenii(unwrapped, "unwrapped.nii"; header=header(phase4D))

romeo needs the TEs to know if the phase images are from multi-echo with different echo times or from epi with identical echo times for each echo.

Masking

romeo does not need a mask, however, if you want to create one:

  • robustmask creates a mask from the magnitude (but I think you mentioned not having a magnitude?)
  • a mask from the phase via romeo:
using MriResearchTools
qmap = romeovoxelquality(phase; TEs=ones(size(phase, 4)))
mask = mask_from_voxelquality(qmap, 0.5) # threshold between 0 and 1

Julia

You can type all these commands directly in the REPL or write them in a function and call with include like you did. If you get serious with julia, I would recommend to use VS code with the julia extension. There you can interactively work on writing scripts similar to matlab.

@korbinian90
Copy link
Owner

And your case of two disconnected regions might require some more tuning of romeo. You can have a look at ?romeo and try out the different options, otherwise I'm happy to help!

@drswgw
Copy link
Author

drswgw commented Mar 3, 2022

Hi Korbinian,
The Julia version is quickly reaching a critical mass, so that's nice. Slight correction, I do have magnitude images to draw from, they are mostly all the same, so typically we average those across the time domain and use those for mask construction e.g. a logical mask in matlab and cast those to double. Sometimes we will rearrange the matrix to take out the empty space, but that's kind of a lot of work for routine stuff. We do pig brains, human brains, phantoms, bioprints etc. so masking is an important part of things for us and one size most definitely does not fit all. I think I can supply the mask (as a .nii file) and put it in my julia_files folder along with the script(s).
It's all about motion encoding, so the question is what does ROMEO do with the TE's. Our case is nominally close to the EPI case, all the echoes are acquired at a fixed interval, but the data in the phase domain is responding to an entirely different set of timings.....more like FMRI I suppose...with asynchronous tasks, in this case it's for heart driven motions...two clocks...one for acquisition...one for the heart....there are a variety of gating approaches...we are trying to do image processing INSTEAD of gating.....e.g. difference between breath hold (gated in a very draconian way) and free breathing (largely ungated). Since stopping the heart is not an option...here we are.
One thing that kills us is offsets....the average phase for the original portions of the image that are not wrapped is in fact data for us...so moving that original average phase around is not ideal. We would rather preserve that then offset the wrapped portions either UP by 2 pi, 4 pi, 6 pi or DOWN by 2 pi, 4 pi, 6 pi. That's perfect for us. If we can do THAT, then we can move on to decoding what time it is in the acquisition frame and in the heart frame, which ultimately gives us something a bit like CINE, except with NUMBERS....as to just what the phases are, at a given time, in a QUANTITATIVE manner...it's a phase movie, with numerical values...that's the goal state.
Will work on implementing the Julia side, and maybe look into VS. Another option is to pass to Julia from Matlab.
For now, keeping it old school with gedit.

@drswgw
Copy link
Author

drswgw commented Mar 3, 2022

Hi Korbinian,
Here is the acquisition clock.....but this is NOT the heart clock....although the slice timing is directly related to the heart clock...kind of like a modulo thing e.g. moduloheart(slice timing
Selection_035
)

@drswgw
Copy link
Author

drswgw commented Mar 3, 2022

Anyway,
Not sure I understand what ROMEO is doing with TE = [1,1,1,1....] or TE = [1,2,3,4,....] the latter is what I have been using up till now. In terms of the right timing from the signal side...it probably looks something like (scale of 0-1 of the heart cycle.....[0.3, 0.45, 0.17, 0.22, 0.96, 0.11, 0.44, 0.34, 0.71.......]....not like a tango at all.
On a certain level, it might make more sense to feed all the slices independently as one offs.....IF ROMEO is trying to match phases across 'times' e.g. phase at t1 matches phase at t2...that's a disaster....all the 'cuts' are jump cuts in time right now...so there is NO obvious relationship in time between adjacent acquisitions...i.e. asynchronous acquisition.

@drswgw
Copy link
Author

drswgw commented Mar 3, 2022

Hi Korbinian,
ROMEO does not need a mask? So what happens when it tries to unwrap the regions of the image that are random phases?

@korbinian90
Copy link
Owner

korbinian90 commented Mar 3, 2022

Not sure I understand what ROMEO is doing with TE

Two things:

  1. To improve the weights that guide unwrapping with the first two volumes. It looks for phase consistency across time. If the second volume has a longer echo time, this needs to be considered. It usually greatly helps the unwrapping
  2. To use temporal unwrapping. By default, only the second volume in spatially unwrapped and starting from that all other volumes are unwrapped. As long there is significantly less than π phase evolution, this works fine.

-> the output phase is still only changed by multiples of 2π. No weird scaling is happening in ROMEO

ROMEO does not need a mask? So what happens when it tries to unwrap the regions of the image that are random phases

No mask is required
ROMEO starts with easy-to-unwrap regions with well behaved phase and only later goes into these random regions. It will mess up greatly there, but this doesn't influence any of the "good" areas. If the image consists of multiple parts seperated by noise, you probably have to set the seeds parameter to a higher value. Otherwise the unwrapping has to cross noise.

@korbinian90
Copy link
Owner

korbinian90 commented Mar 3, 2022

I didn't fully understand what your effective TE is. So what romeo assumes is that phase evolves linearly with time. For longer echo time, the phase changes are bigger. If the "fMRI" phase changes are small compared to π, it is probably fine using TEs=ones(...). If these changes are quite large, it would probably help if you add proportionality factors as TEs.

I think best is to start with the default romeo configuration of "4D with temporal unwrapping"

If that doesn't give satisfying results, you can try "4D without temporal unwrapping (unwrap-individual flag)"

And if that fails you could try separate 3D unwrapping, but to avoid ending up with 2pi jumps between volumes, it is probably best to use the correctglobal flag

@drswgw
Copy link
Author

drswgw commented Mar 3, 2022

Perfect, a lot to chew on, but enough to move forward...cool!
Onwards from "hellojulia.jl" to "unwrap.jl".
I think I will have to try a couple of things and compare those to make sure the results are consistent.

@korbinian90
Copy link
Owner

btw, I added documentation to my packages ROMEO and MriResearchTools, accesible via the little badge docs|dev (or here: https://korbinian90.github.io/MriResearchTools.jl/dev/)

@drswgw
Copy link
Author

drswgw commented Mar 3, 2022

Hi Korbinian,
First go round - only so-so.
Need to force several things to move forward...first....apply the mask, and have romeo not unwrap outside the mask, and display the data MASKED.
As near as I can tell without a mask...romeo is folding the phases the WRONG way....the flow is down then up....or up then down...relative to the gradient, so one side should be darker than the other throughout.
Where the flow is concentrated, things should speed up i.e. get whiter (or blacker) depending on direction.
seems like all the values near pi or -pi are being wrapped towards zero, or the base level is being shifted.
Consider a mountain.....the data starts...partway up the mountain....a correct unwrapping should yield a topo map, everything BELOW the starting point should be LESS, and everything ABOVE the starting point should be MORE. This current unwrapping isn't doing that...or at least I don't think it is, instead it all seems to be getting compressed towards the starting point/average.
Selection_039
Selection_038
Selection_037
Selection_036

@drswgw
Copy link
Author

drswgw commented Mar 3, 2022

Do I need to scale the initial spectra to -pi to pi, that is an acceptable, or acceptable enough, version of how the data really is.....the last frame above on the lower left looks right, darker on one side, lighter on the other, that's pre-unwrapping. And this one below looks OK too. If I was going to guess, I'd say some regions are unwrapping WELL, and other regions, not so well
Selection_040
.

@drswgw
Copy link
Author

drswgw commented Mar 3, 2022

Definitely promising, but not optimized yet, at least I don't think so.

@drswgw
Copy link
Author

drswgw commented Mar 3, 2022

This looks better, well it doesn't 'look' better, but it seems better, more consistent, more robust

Selection_043

Selection_041

Took a little while to get the syntax, and still don't know how to include a mask to guide the unwrapping...and not sure if the mag image is being used or not......but...tentatively....better and/or moving forward! The numbers look wrong though...I think it is expecting the input in radians, so there is something funky going on with the scaling...should start [-pi,pi] and probably end something like [-5 pi, 5 pi]

@drswgw
Copy link
Author

drswgw commented Mar 3, 2022

Hi Korbinian,
Thought about it a little bit and what is needed is FIXED scaling. The data starts as 12 bit ([0,4095]) and typically is rescaled slightly to yield ([-4095,4095])...I think that's what the DICOM to NIFTI conversion is doing, and that's all copasetic. From there probably needs to be FIXED SCALED down to [-pi, pi] before unwrapping. Variable scaling ([min, max]) => [-pi,pi] won't work because some data is wrapped, while other data is not, the data is intrinsically scaled (by the gradients) so the original [0,4095] => [-4095,4095] => [-pi, pi] is all fine as long as it's a FIXED scaling.
I can do that outside Julia, but it would be nice to be able to do fixed scaling inside Julia.....
The cast to 16 bit and later on to double etc. doesn't change the original [0,4095] data density.

P.S. All the help is moving this along very nicely and rapidly. Liking the Julia interface a lot, and it runs FAST!

@drswgw
Copy link
Author

drswgw commented Mar 4, 2022

MUCH BETTER! BAM!
Selection_044
Selection_045

Still need to get better control of the masking (importing a NIFTI mask so far is not working, the default type for the mask in ROMEO is apparently Bool, while for NIFTI it is float/double...or more precisely from Matlab to NIFTI).
And better control over the scaling - fixed not variable.
Great progress!

@drswgw
Copy link
Author

drswgw commented Mar 4, 2022

Looks like it has shifted the original data away from the starting values...that's NOT ideal....but there are more gradients possible - this is a simple all plus gradient - but +/- are possible which allows for a subtraction later....still even in that case, the DC term is not meaningless but carries data, so would prefer not to mess with the DC component......the challenge is how to turn things OFF in ROMEO....turn it ALL OFF...then bring back ON states one at a time...that would be more useful. i.e. the initial data average was around pi/2 on one side and around pi on the other...the true mean is around 3 pi/4.....the unwrapped data has a mean near zero.....the 3pi/4 true mean has been REMOVED....the raw unwrapped data should look something like pi/2 - 5pi/4.....of course I can sacrifice the DC term if I must....but I'll be sad to see it go....back to the topo map...the top of Everest after shifting has zero elevation...so climbing it is...zero (not strictly true, because the bottom would be negative) but it's meant to be a metaphor about the importance of DC terms.
Still, this is increasingly workable...so can move on towards 'fmri' timing...yay! Getting happy!

@drswgw
Copy link
Author

drswgw commented Mar 4, 2022

Can't import or export masks......
the BitArray{4} julia type is not apparently compatible with NIFTI, or at least not obviously.
Can't turn off pre-scaling and replace it with my own absolute scaling scal*Phase4D
Can't turn off zero shift
Selection_046

@drswgw
Copy link
Author

drswgw commented Mar 4, 2022

Purely cosmetic touches, but moving towards something easier to run/play with...
the JSON module is to pull slice timings from the NIFTI prequel/header via MRIcroGL (which is in JSON).
Selection_047
Selection_048
Selection_051

.

@korbinian90
Copy link
Owner

korbinian90 commented Mar 4, 2022

A lot to read and understand, I don't have a lot of time, so I will answer some parts
Glad that it is moving along!

Masking

I think masking or not masking the data doesn't really influence the values inside your object after unwrapping, only in the background noise. I think this problem could be solved by adapting the scaling inside the viewer. Another option would be to mask after unwrapping with something like:
unwrapped = unwrapped .* mask or unwrapped[mask] .= 0
The datatype of the mask shouldn't matter for romeo. But you can just create a binary mask with:
binary_mask = mask .== 0
(The . is like in matlab, but is generalized. It works nearly everywhere to make stuff run voxelwise on an array)

Scaling

The readphase() function should perform quite good scaling. You can compare it by just saving the image again. The output of phase = readphase(...) is a struct with header and image. You can access the unscaled values via phase.raw and scale them yourself with scaled_phase = phase.raw .* (pi / 2048). And best to save it back to disk and check if the range is 2pi afterwards.

Gradient

ROMEO shouldn't be able to change a gradient in the phase as it adds/subtracts only 2pi from individual voxels. If there is an unwrapping error in the object, it should be clearly visible as a discrete phase jump of some voxels.

@korbinian90
Copy link
Owner

korbinian90 commented Mar 4, 2022

As your data is not wrapped in most part, the unwrapped output should be identical to the input phase in large areas. You could confirm that ROMEO does what you want with:

phase = readphase(...)
unwrapped = romeo(phase, ...)
difference = phase .- unwrapped
# difference .*= mask # optional masking of difference image to avoid confusing background
savenii(difference, "difference.nii")

Viewing the difference.nii should show probably 0 in most of your object and 2pi / -2pi at wrapped parts. In noise the values get prabably large and random.

@drswgw
Copy link
Author

drswgw commented Mar 4, 2022

Cool, all helpful...will play on!

@drswgw
Copy link
Author

drswgw commented Mar 5, 2022

Hi Korbinian,
Took a while to get Julia to plot anything, documentation is pretty bad/conflicting/confusing. For Ubuntu 16, and the python versions it uses + what I am running in this environment, matplotlib tops out at about vsn 3.0, so gave up on using the pyplot backend, the GR backend is completely hopeless (can only launch a single plot window), and plotlyjs is mediocre (can't resize plots after they are drawn, can probably pre-size...just a detail right now)....plotting is only an aid along the way...therefore plotlyjs is good enough.
Working on slice timing, ultimately need to use the slice timing to reorder the unwrapped data in time...and thus check if the unwrapping has affected the temporal response.
Since the unwrapping is using constant timing...it may be that the unwrapping will be better if I reorder first...we shall see.....trying to do that reordering in Julia, and it's going OK for now....I'm a hacker not a coder...so elegant...not....but getting pretty happy so far.

Selection_053
Selection_054
Selection_055

@drswgw
Copy link
Author

drswgw commented Mar 16, 2022

Hi Korbinian,
Made some real progress here. The julia code is pretty messy still, so I probably won't post it here - our application is pretty niche. If there's interest I'm not against posting it. Anyway...here is the update....
DATA AFTER TEMPORAL UNWRAPPING (note stripes left image coronal and only image sagittal - from asynchronous acquisition/slice timings, 2 sine waves per slice, alternating UP/DOWN phases). Data is acquired axial, but the slice timings manifest as phase offsets from slice to slice...this is a pretty common problem!
unwrp

DATA AFTER RESORTING/RETIMING FOR OUR APPLICATION (note stripes are gone now, and only 1 sine wave per slice, continuous phase direction from slice to slice...SUCCESS!)
srt_ret_unwrp

Residual issues are mostly to do with optmizing the ROMEO unwrapping. For instance comparing spatial unwrapping with temporal unwrapping, sort AFTER unwrapping, sort BEFORE unwrapping, play with other parameters etc. All very happy, Julia was the key....cutting out the middle man made a big difference here. Could probably port it all back to Matlab calls...but why...as a Julia "app" very happy...NIFTI IN => unwrapped/sorted OUT....BAM! issue closed

P.S. If you don't mind leaving your comments up, still working through/along those suggestions, very relevant to what I am doing. So long and thanks for all the fish!

@drswgw
Copy link
Author

drswgw commented Mar 16, 2022

SrtRetUnwrp

@drswgw
Copy link
Author

drswgw commented Mar 16, 2022

WOW! BEAUTIFUL! (still some tweaking to do...but looks really good!)

@korbinian90
Copy link
Owner

Wow, looks very nice! Really cool video
Great that you came back to report on the progress, I was wondering if you were getting good results.

In the video, there are some individual voxels at the edge of the top part of the object, which jump between black and white. Temporal unwrapping is supposed to keep those voxels from jumping around by always unwrapping into the same direction.
I'm not sure if it is a problem for you, though.

P.S. I will just keep this open so all comments stay there ;)

@korbinian90
Copy link
Owner

If borders of the brain or tissue interfaces ring that is expected, but if phase wraps ring, that means that the scanner reconstruction is messing up the data. I would love to help to fix it afterwards in romeo, but romeo is heavily relying on the fact that phase wraps are between -pi and pi in directly neighboring voxels. This is the central assumption. You could just smooth the unwrapped image, but that would destroy high frequency/small structure information.

Maybe it is a setting in reconstruction on the scanner that you can change. There might be a filter that can be switched off or sometimes data is interpolated and written out in twice the resolution (for no obvious reason to me).

@drswgw
Copy link
Author

drswgw commented Sep 28, 2022

Hi Kn,
It's a WIP, so not everything can be modified, but I will poke around.
I have a SMALL amount of TWIX data, but that is much more daunting.
So to restate what you are telling me...give me Heaviside, or give me death.
I'll post the DICOM here in a bit....that's worth following up on. I am about 90% certain that MAG and PHASE are the only available export options, but who knows for sure without getting out the microscope.

At this moment, I will probably do CANNY edge or something and try to repair the ringing at the phase crossover. It's NOT an ideal solution, but it moves the football. There's data in the can, then there's new data. There's a lot of data in the can, so I am all about identifying and patching as we move forward. Or said a different way, the perfect is the enemy of the good.

I'm guessing here, but there is probably a filter that gets applied in order to generate the 12 bit export data from what nominally should be a higher bit count for the internal machine data.

How about adaptive coil combine? That is a must for us, and this is 64 Ch data, that CERTAINLY looks like phase averaging???

@korbinian90
Copy link
Owner

adaptive coil combine should be fine. It computes coil combination in complex. At high field strengths (7T) it can produce open-ended fringe lines, but I haven't seen them in your data. So adaptive coil combine is for sure not the culprit. It must be something later in the chain.

Usually you can look at the functor chain by opening "twix" on the Siemens scanner, clicking the dat file of the scan, opening it (I think edit or something). Then clicking the pipeline. It must be a functor after "extractmodecombine" -> "extractPHS" -> ... that messes up the phase data

@drswgw
Copy link
Author

drswgw commented Sep 28, 2022

Dicom data (P dirn only) posted at https://github.com/drswgw/MRE_DATA.git

it's all .7z (not bad after compression). First folder is MAG, 2nd folder is PHASE

We are 3T Prisma

@korbinian90
Copy link
Owner

weird.. that looks fine to me
grafik
I'll try to convert to nifti and run through romeo..

@korbinian90
Copy link
Owner

korbinian90 commented Sep 28, 2022

Ok, I found it in the DICOM..
grafik
grafik

@drswgw
Copy link
Author

drswgw commented Sep 28, 2022

Cool, so this is artifact and not waves running in/out from the beach right around pi?
It's echo planar, so the signal is shifting across the k space lines (although I would tend to think of that as global not local).
This seems very local.
So to summarize, good phase crossover is Heaviside, bad phase crossover is ringy. Now I just have to figure out how to repair the ringy parts, so the whole thing can get unwrapped. My good signal is only about 10% of the resting phase (or less).
Seems like a good start might be to construct some rays from the center and take a look at the 1D version of the 2D image.
Even just straight along x, straight along y would be a start. I don't think there is a tissue structure there, it's just where the resting phase is near pi to begin with.
Do I have it right, that ROMEO sees the ringy parts as requiring no unwrapping? Or just PART of the ringy parts?

@drswgw
Copy link
Author

drswgw commented Sep 28, 2022

Here's a 1D look at the ringing artifact.
Ignore the entry from the mask to the brain, that doesn't matter, and I can erode a voxel or two out there if needed.
It's the interior phase crossover that is of interest.
It looks like it rings or doesn't depending on the particular part of the data.
I don't think I have enough data to throw the ringy parts out...maybe?
But it does look identifiable...it rings at TOP and BOTTOM.

Not Ringing
not_ringing

Ringing
ringing

@drswgw
Copy link
Author

drswgw commented Sep 28, 2022

hi Kn,
This is productive for me, uses simple threshold based unwrapping (phsn < pi => phsn + 2*pi).
The artifact then looks exactly like a sinc function, that drifts across the data.
My data (generally) is in the spread over time, that is phi(t), although I need to drill down into the big split as to whether that is real or not. This is data for 40 random time points.
The rogue voxels at the edge of the brain I can live with.
Anyway, removing the sinc functions...either in post processing or in data acquire is the path forward.
Something is causing that corruption of the crossover region.

A nice expose of the problem.
Thrsh_UW_slc35

For comparison: a much cleaner region
cleaner_thrsh_UW

@drswgw
Copy link
Author

drswgw commented Sep 29, 2022

On more careful examination, it looks like only the phase wrap direction (AP i.e. up and down in the image) generates the artifact, and only under certain sub-conditions. So a Y scan reduces everything to 1D.
VERY IMPERFECT, but finding the sinc artifacts, fitting them with a sinc interpolation (of fixed voxel width) and then 'turning down' the amplitude of the sinc fit in those regions seems like the best compromise. It gives a rigorous fit, the artifact can then be turned up or down (within reason), and it 'repairs' things well enough to move forward. Better than trying to cut or bridge with a linear fit. Fixing the data on the scan side would be even better, but this data is what it is, so a post-processing solution that is not too invasive is attractive. Bosses always wanta see results! Even if imperfect.

quick look to see if the 'sinc artifact' fits a sinc function
sinc_w_data

Matlab based sinc interpolation
sinc_xmpl1

sinc_xmpl2

I'll have a crack at coding this up, then get back to the ROMEO unwrapping.

Then more heavy lifting with the time syncing....all moving forward.
This is a big deal, because it was KILLING the unwrapping.
It also fixes 'corrupted phases'...working man's fix.

Always good to get another point of view

@drswgw
Copy link
Author

drswgw commented Sep 30, 2022

no new code, but new data, I'll post it up in a little bit.
Think the 'phase corruption' is solved.
good news bad news though...the 'fix' turns OFF some very common parameters.

@drswgw
Copy link
Author

drswgw commented Sep 30, 2022

new data.... P_9_29_22.7z, R_9_29_22.7z, S_9_29_22.7z posted at https://github.com/drswgw/MRE_DATA.git

@drswgw
Copy link
Author

drswgw commented Oct 3, 2022

MRI_Tools 3.5.5 => matlab caller is indeed fixed.
What would help a lot is if you expanded the help pages to include the calling syntax in more detail...

for instance using max seeds as an example.....

from the matlab caller I can now call it from auxilary commands as '-s 32'
but have no clue as how to call it from the running text...
e.g. parameters.phase_offset_correction = 'off';
is it 'parameters.max_seeds = 32' ......that is by no means obvious, because there is no list of commands.
same with julia commands...just not listed with very much detail/examples.

usage: [-p PHASE] [-m MAGNITUDE] [-o OUTPUT]
[-t ECHO-TIMES [ECHO-TIMES...]] [-k MASK [MASK...]]
[-u] [-e UNWRAP-ECHOES [UNWRAP-ECHOES...]]
[-w WEIGHTS] [-B]
[--phase-offset-correction [PHASE-OFFSET-CORRECTION]]
[--phase-offset-smoothing-sigma-mm PHASE-OFFSET-SMOOTHING-SIGMA-MM [PHASE-OFFSET-SMOOTHING-SIGMA-MM...]]
[--write-phase-offsets] [-i] [--template TEMPLATE]
[-N] [--no-rescale] [--threshold THRESHOLD] [-v] [-g]
[-q] [-Q] [-s MAX-SEEDS] [--merge-regions]
[--correct-regions] [--wrap-addition WRAP-ADDITION]
[--temporal-uncertain-unwrapping] [--version] [-h]

Now possibly this makes sense to you, but it makes little if any external sense, because the syntax is not demonstrated, elucidated, exampled. Manuals written by people who already know how to use the program...generally...are largely entirely incomprehensible to people who do not know how to use the program.

Heres a code snippet in Haskell (which by the way I gave up on as a programming language, because it was so incomprehensible) to try and get the point across. It almost makes sense...but it's just not obvious how the syntax works.

haskell

Now there is probably more in the help pages....but without examples the syntax is still pretty OPAQUE

Which is just me bitching about the new data set still has the same phase overlap issues (I think), although some other aspects are looking better. Will have to dig in more detail to know for sure.

@drswgw
Copy link
Author

drswgw commented Oct 7, 2022

Hi Kn,
Plugging along...thanks for fixing the matlab caller.
Tell me more about phase corruption....currently I can't fix it....and I can't find the filter...at least not obviously.
My output formats are MAG, PHS, or MAG/PHS...so I am stuck in 'wrapped' space, sinc artifact and all.
I can desinc it, but that actually will just make things worse.
Interpolation is about the only remaining option as a 'fix'.

@korbinian90
Copy link
Owner

I think the phase corruption is only possible to fix at its source... Can you talk to the developers of the wip sequence? This clearly seems like a bug in their code.
Do you know how to access the functor pipeline of your sequence in twix? There might be clues to what is corrupting your phase
Screenshot of functor pipeline. Up to extractmodecombine everything is handled in complex, so no issues. After extractPHS (not activated in the run of the screenshot), scaling or averaging the phase causes problems.
image

@korbinian90
Copy link
Owner

And yes, planning to add a bit better documentation and more examples to romeo, just didn't find the time yet

@drswgw
Copy link
Author

drswgw commented Oct 9, 2022

Hi Kn,
No I'm functor clueless.
If it's part of IDEA, I don't think we have that.
I just have the two back door commands...."twix" and "mdb" (I think that's => it's the twix viewer thingie).

@korbinian90
Copy link
Owner

The 'twix' command on the scanner is the one I'm talking about, no IDEA required.
In twix you can select the sequence you just measured and in the top toolbar you have to press a button (which from memory has three parts with some red in it). This opens a menu at the bottom where you can click "edit" and start the retro reconstruction. When you click "edit" another window opens showing the protocol in xbuilder.
image
This should look similar to this (the left side). There if you scroll far down you find usually two red pipeline blocks, one of those blocks has the interesting retro recon functors. When you select PIPE, like in the screenshot, you can see the pipeline to the right. There, you could try to find out what's messing up the phase, maybe..
If you know what's causing it, you might be able to deactivate that part..hopefully

@drswgw
Copy link
Author

drswgw commented Oct 9, 2022

Hi Kn,
Very cool. I only use twix for data export. Ironic because I can't do much with the twix files later.. (we mentinoed ISMRM so that's starting to heat up), besides hope springs eternal.
Can this do retro recons??? i.e. can I edit the pipeline? e.g. make a real + imag export from the kspace data?
Or is it just a viewer?
Either way...pretty cool.

@korbinian90
Copy link
Owner

It can do retro recon, and you can change a bunch of settings. However, only what was intended by the programmers who wrote the pipeline and you have to know which parameter to change..
For some functors you can click on them in the left menu and change settings

@drswgw
Copy link
Author

drswgw commented Oct 14, 2022

Hi Kn,
Here's part of the WIP pipe/functor chain. A lot of duplication, but the fonts are super small, and the boxes super big.

local_pe_filter_start

IMG_20221013_151340317

cleanup

extract_big

end_big

@drswgw
Copy link
Author

drswgw commented Oct 14, 2022

From what you said, I am kind of wondering what the Maxwell correction is doing...seems weird that it's AFTER the conversion to PHS/MAG.
Have no clue how to turn things ON/OFF in the pipe/functor chain e.g. is it even possible to turn OFF the Maxwell correction, or move it BEFORE the MAG/PHS extraction?

@korbinian90
Copy link
Owner

Yes, exactly, it looks like only the Maxwell correction functor can be the culprit to mess up the phase.

I think moving around in the functor chain is not possible, but it might be possible to switch it off. Two ideas:

  1. Look through all sequence settings in the standard UI when setting up the sequence as usual. There might be a checkbox to deactivate maxwell correction
  2. I the window where you see the functor chain, you can click in the left pane on localpefilter_ps->EPIMaxwellFunctor and there you might be able to change some settings. There might be a simple flag like ON/OFF or something like that. After changing this setting you have hit save in this window and then retro reconstruct from twix

@drswgw
Copy link
Author

drswgw commented Nov 1, 2022

Hi Kn,
I think setting the execute flag to false should do it.
But whether the downstream components will still be connected...I don't know.
Also...will I get a chance to rename the output...or will it overwrite the other output?

maxwell functor
maxwell

@drswgw
Copy link
Author

drswgw commented Nov 10, 2022

Hi Kn,
Was able to generate a retro recon with _Execute set to false. Have no clue what that did yet, so far it seems like nothing. The functor chain is not obviously changed. I think that's the problem, to STOP doing the Maxwell Correction, one would need to change the functor chain. Basically the Maxwell Correction is subtracting out the contribution from off axis gradient components, which is mostly desirable. On the good news side, the retro recon does NOT overwrite the original recon, instead it slaps _RR on the end

Retro_Recon

@drswgw
Copy link
Author

drswgw commented Jul 11, 2023

Hi Korbininan,
Something new...trying to unwrap Fourier components without much luck.

perform unwrapping...
ERROR: AssertionError: Unwrap-weights are all zero!
Stacktrace:
[1] unwrap!(wrapped::SubArray{Float32, 3, Array{Float32, 4}, Tuple{Base.Slice{Base.OneTo{Int64}}, Base.Slice{Base.OneTo{Int64}}, Base.Slice{Base.OneTo{Int64}}, Int64}, true}; regions::Array{UInt8, 3}, keyargs::Base.Iterators.Pairs{Symbol, Any, NTuple{10, Symbol}, NamedTuple{(:phase2, :TEs, :maxseeds, :weights, :correctglobal, :merge_regions, :mag, :mask, :correct_regions, :wrap_addition), Tuple{Array{Float32, 3}, Vector{Int64}, Int64, Symbol, Bool, Bool, Array{Float32, 3}, BitArray{3}, Bool, Float64}}})
@ ROMEO C:\Users\runneradmin.julia\packages\ROMEO\3D8Ja\src\unwrapping.jl:3
[2] unwrap_individual!(wrapped::Array{Float32, 4}; TEs::Vector{Int64}, keyargs::Base.Iterators.Pairs{Symbol, Any, NTuple{9, Symbol}, NamedTuple{(:merge_regions, :correct_regions, :wrap_addition, :maxseeds, :weights, :correctglobal, :mag, :mask, :regions), Tuple{Bool, Bool, Float64, Int64, Symbol, Bool, Array{Float32, 4}, BitArray{3}, Array{UInt8, 3}}}})
@ ROMEO C:\Users\runneradmin.julia\packages\ROMEO\3D8Ja\src\unwrapping.jl:154
[3] unwrap!(wrapped::Array{Float32, 4}; TEs::Vector{Int64}, individual::Bool, template::Int64, p2ref::Int64, temporal_uncertain_unwrapping::Bool, keyargs::Base.Iterators.Pairs{Symbol, Any, NTuple{9, Symbol}, NamedTuple{(:merge_regions, :correct_regions, :wrap_addition, :maxseeds, :weights, :correctglobal, :mag, :mask, :regions), Tuple{Bool, Bool, Float64, Int64, Symbol, Bool, Array{Float32, 4}, BitArray{3}, Array{UInt8, 3}}}})
@ ROMEO C:\Users\runneradmin.julia\packages\ROMEO\3D8Ja\src\unwrapping.jl:70
[4] unwrapping(data::Dict{String, AbstractArray}, settings::Dict{String, Any}, keyargs::Dict{Symbol, Any})
@ RomeoApp C:\Users\runneradmin.julia\packages\RomeoApp\RGmfi\src\caller.jl:227
[5] unwrapping_main(args::Vector{String})
@ RomeoApp C:\Users\runneradmin.julia\packages\RomeoApp\RGmfi\src\caller.jl:21
[6] julia_main()
@ RomeoApp C:\Users\runneradmin.julia\packages\RomeoApp\RGmfi\src\RomeoApp.jl:12
[7] top-level scope
@ none:1
Error using ROMEO

image

Tried converting to single, adding pi...still no good.
Seems like the -pi to pi as a float (single or double) is incomprehensible?

Maybe I need to use unmasked data? Or throw out the DC component?
I am trying to fool Romeo into accepting the FT comps across the different gradients as echoes with individual unwrapping. I don't think it's the data per se...I think it's a Romeo input foramt thing...this is via the Matlab front end

onephs = ones([1,szphs(4)]);
parameters.TE = onephs; %onephs; % [1,2,3]; % required for multi-echo
parameters.mag = load_nii(magfils{aa}).img;
parameters.mask = 'robustmask'; % load_nii(mask_fn).img; % can be an array or a string: 'nomask' | 'robustmask' | 'qualitymask'
parameters.calculate_B0 = true; % optional B0 calculation for multi-echo
parameters.phase_offset_correction = 'off'; % options are: 'off' | 'on' | 'bipolar'
parameters.voxel_size = [3.5,3.5,3.5]; %load_nii_hdr(phase_fn).dime.pixdim(2:4); % if set the written NIfTI files will have the matching voxelsize; is also used for optimal kernel size in MCPC-3D-S phase offset smoothing; can be given as [0.3, 0.3, 1.2]
%
% -i is individual unwrapping
parameters.additional_flags = '-Q -i -v -s 8 --template 2'; % '-q -i'; % settings are pasted directly to ROMEO cmd (see https://github.com/korbinian90/ROMEO for options)
%
% %% Suggested steps
mkdir(parameters.output_dir);
addpath(parameters.output_dir);
%
[unwrapped, B0] = ROMEO(phase, parameters);

@drswgw
Copy link
Author

drswgw commented Jul 11, 2023

OK, FIXED!
Threw out the initial DC component and the Middle low intensity component as well as all the non-unique components (symmetry of FT). Added a specific mask (instead of robust mask).
Not sure if offsetting by pi i.e. [0,2pi] versus [-pi,pi] matters...but it's running now.

@korbinian90
Copy link
Owner

Interesting.. I would have assumed that the robustmask algorithm wasn't robust enough and failed.
Good that you found a solution!

@tayebehebrahimi64
Copy link

Hello,
I am completely new in julia and that's why I want to run romeo via matlab in linux. But I get an error says:

Error using ROMEO
ROMEO unwrapping failed! Check input files for corruption in
/N/project/dMRI1/CARE_CSI/ANALYSIS/QSM/temp/Tina/mritools_ubuntu-22.04_3.6.6/bin/romeo_tmp

could you please help me to fix it?
Thank you

@drswgw
Copy link
Author

drswgw commented Jul 15, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants