demo_read.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import numpy as np
  2. from matplotlib import pyplot as plt
  3. import pypulseq as pp
  4. """
  5. Read a sequence into MATLAB. The `Sequence` class provides an implementation of the _open file format_ for MR sequences
  6. described here: http://pulseq.github.io/specification.pdf. This example demonstrates parsing an MRI sequence stored in
  7. this format, accessing sequence parameters and visualising the sequence.
  8. """
  9. # Read a sequence file - a sequence can be loaded from the open MR file format using the `read` method.
  10. seq_name = "epi_rs.seq"
  11. system = pp.Opts(
  12. B0=2.89
  13. ) # Need system here if we want 'detectRFuse' to detect fat-sat pulses
  14. seq = pp.Sequence(system)
  15. seq.read(seq_name, detect_rf_use=True)
  16. # Sanity check to see if the reading and writing are consistent
  17. seq.write("read_test.seq")
  18. # os_system(f'diff -s -u {seq_name} read_test.seq -echo') # Linux only
  19. """
  20. Access sequence parameters and blocks. Parameters defined with in the `[DEFINITIONS]` section of the sequence file
  21. are accessed with the `get_definition()` method. These are user-specified definitions and do not effect the execution of
  22. the sequence.
  23. """
  24. seq_name = seq.get_definition("Name")
  25. # Calculate and display real TE, TR as well as slew rates and gradient amplitudes
  26. test_report = seq.test_report()
  27. print(test_report)
  28. # Sequence blocks are accessed with the `get_block()` method. As shown in the output the first block is a selective
  29. # excitation block and contains an RF pulse and gradient and on the z-channel.
  30. b1 = seq.get_block(1)
  31. # Further information about each event can be obtained by accessing the appropriate fields of the block struct. In
  32. # particular, the complex RF signal is stored in the field `signal`.
  33. rf = b1.rf
  34. plt.subplot(211)
  35. plt.plot(rf.t, np.abs(rf.signal))
  36. plt.ylabel("RF magnitude")
  37. plt.subplot(212)
  38. plt.plot(1e3 * rf.t, np.angle(rf.signal))
  39. plt.xlabel("t (ms)")
  40. plt.ylabel("RF phase")
  41. # The next three blocks contain: three gradient events; a delay; and readout gradient with ADC event, each with
  42. # corresponding fields defining the details of the events.
  43. b2 = seq.get_block(2)
  44. b3 = seq.get_block(3)
  45. b4 = seq.get_block(4)
  46. # Plot the sequence. Visualise the sequence using the `plot()` method of the class. This creates a new figure and shows
  47. # ADC, RF and gradient events. The axes are linked so zooming is consistent. In this example, a simple gradient echo
  48. # sequence for MRI is displayed.
  49. # seq.plot()
  50. """
  51. The details of individual pulses are not well-represented when the entire sequence is visualised. Interactive zooming
  52. is helpful here. Alternatively, a time range can be specified. An additional parameter also allows the display units to
  53. be changed for easy reading. Further, the handle of the created figure can be returned if required.
  54. """
  55. # seq.plot(time_range=[0, 16e-3], time_disp='ms')
  56. """
  57. Modifying sequence blocks. In addition to loading a sequence and accessing sequence blocks, blocks # can be modified.
  58. In this example, a Hamming window is applied to the # first RF pulse of the sequence and the flip angle is changed to
  59. 45 degrees. The remaining RF pulses are unchanged.
  60. """
  61. rf2 = rf
  62. duration = rf2.t[-1]
  63. t = rf2.t - duration / 2 # Centre time about 0
  64. alpha = 0.5
  65. BW = 4 / duration # Time bandwidth product = 4
  66. window = 1.0 - alpha + alpha * np.cos(2 * np.pi * t / duration) # Hamming window
  67. signal = window * np.sinc(BW * t)
  68. # Normalise area to achieve 2*pi rotation
  69. signal = signal / (seq.rf_raster_time * np.sum(np.real(signal)))
  70. # Scale to 45 degree flip angle
  71. rf2.signal = signal * 45 / 360
  72. b1.rf = rf2
  73. seq.set_block(1, b1)
  74. # Second check to see what has changed
  75. seq.write("read_test2.seq")
  76. # os_system(f'diff -s -u {seq_name} read_test2.seq -echo') # Linux only
  77. # The amplitude of the first rf pulse is reduced due to the reduced flip-angle. Notice the reduction is not exactly a
  78. # factor of two due to the windowing function.
  79. amp1_in_Hz = max(abs(seq.get_block(1).rf.signal))
  80. amp2_in_Hz = max(abs(seq.get_block(6).rf.signal))