├── Analysis
├── .gitignore
├── README.md
├── UnpackFromArchive.py
├── DisplayExperimentID.py
├── Comparator.py
├── functions.py
├── DetectWhichImageIsFocusedBest.py
└── TarToArchive.py
├── Demonstrator
├── .gitignore
├── detect_lines_in_checkerboard.py
├── darkimages.py
├── lineprofiler.py
└── undistort_images.py
├── DetectorMockup
├── .gitignore
├── README.md
├── ScintillatorBackplates.scad
└── OmmatiDiag.scad
├── aptina
├── data
│ ├── board_data
│ │ ├── MI_1002.cfg
│ │ ├── MI_100B.cfg
│ │ ├── Demo3_5551.cdat
│ │ ├── HDMI_DemoLC.cdat
│ │ ├── HDMI_DemoLCX.cdat
│ │ ├── Demo2_FPGA_A3.cdat
│ │ ├── Demo2_FPGA_B0.cdat
│ │ ├── Demo2_FPGA_B2.cdat
│ │ ├── Demo2_FPGA_B3.cdat
│ │ ├── Demo2_FPGA_B4.cdat
│ │ ├── Demo2_FPGA_B5.cdat
│ │ ├── Demo2x_FPGA_C0.cdat
│ │ ├── Demo2x_FPGA_C1.cdat
│ │ ├── HDMI_Demo_00CE.cdat
│ │ ├── HDMI_Demo_C0CE.cdat
│ │ ├── HSSA_Deser_CC.cdat
│ │ ├── HSSA_Deser_CD.cdat
│ │ ├── MIDES_FB_FPGA_A0.cdat
│ │ ├── MIDES_FB_FPGA_A1.cdat
│ │ ├── CameraLinkDemo_5551.cdat
│ │ ├── MI_1007.cfg
│ │ ├── MI_100D.cfg
│ │ ├── MI_100E.cfg
│ │ ├── BigDog_SMFPGA_A.cdat
│ │ └── DualMipiFPGA.cdat
│ └── sensor_data
│ │ └── sensor_desc.xsl
├── README.md
└── NoiseVsExposure.py
├── SyncToAFS.cmd
├── Info
├── triggering.png
├── OTF.md
└── triggering.md
├── elphel
├── ftp_files_to_camera.sh
├── elphel.py
├── gamma.php
├── GPIO_input.py
├── setexposure.php
├── GPIO_output.py
└── globaldiagnostix.php
├── .gitignore
├── .gitconfig
├── GOTTHARD
├── atte_si2.dat
├── Si_Attenuation.dat
└── Photon-Calculation.py
├── PlotBallLenses.py
├── NumericalAperture.py
├── Spectra
├── Xray-Spectrum_040kV.txt
├── Xray-Spectrum_046kV.txt
├── Xray-Spectrum_053kV.txt
├── Xray-Spectrum_060kV.txt
├── Xray-Spectrum_070kV.txt
├── Xray-Spectrum_080kV.txt
├── Xray-Spectrum_090kV.txt
├── Xray-Spectrum_100kV.txt
└── Xray-Spectrum_120kV.txt
├── RunCalculate.sh
├── AbsorptionCoefficients.py
├── README.md
├── LICENSE
├── readAptinaRAW.py
├── nist-attenuation-scraper.py
├── nist
├── water.dat
├── a150.dat
├── adipose.dat
├── lung.dat
├── muscle.dat
├── bone.dat
├── tissue.dat
├── blood.dat
├── gadolinium.dat
└── cesium.dat
├── randomMTF.py
├── DepthOfFocus.py
├── tiscamera
└── setup.md
├── PlotXraySpectra.py
├── Switch.py
├── SurfaceEntranceDoseCalculator.py
├── .screenrc
├── SurfaceEntranceDose.py
├── GenerateTestImage.py
├── FocusFOVViewer.py
├── FocusPlotLine.py
├── MTF.py
├── SetupPi.md
├── MTF_reader_and_plotter.py
└── AngularOpening.py
/Analysis/.gitignore:
--------------------------------------------------------------------------------
1 | *.mp4
2 |
--------------------------------------------------------------------------------
/Demonstrator/.gitignore:
--------------------------------------------------------------------------------
1 | *.jpg
2 |
--------------------------------------------------------------------------------
/DetectorMockup/.gitignore:
--------------------------------------------------------------------------------
1 | dimlines.scad
2 | TextGenerator.scad
3 |
--------------------------------------------------------------------------------
/aptina/data/board_data/MI_1002.cfg:
--------------------------------------------------------------------------------
1 | CHIP_FILE = "BigDog_SMFPGA_*.cdat"
2 |
--------------------------------------------------------------------------------
/aptina/data/board_data/MI_100B.cfg:
--------------------------------------------------------------------------------
1 | CHIP_FILE = "MIDES_FB_FPGA_*.cdat"
2 |
--------------------------------------------------------------------------------
/SyncToAFS.cmd:
--------------------------------------------------------------------------------
1 | rsync -avr * haberthuer@slslc:/afs/psi.ch/project/EssentialMed/Dev/
2 |
--------------------------------------------------------------------------------
/Info/triggering.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/Info/triggering.png
--------------------------------------------------------------------------------
/aptina/data/board_data/Demo3_5551.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/Demo3_5551.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/HDMI_DemoLC.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/HDMI_DemoLC.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/HDMI_DemoLCX.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/HDMI_DemoLCX.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/Demo2_FPGA_A3.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/Demo2_FPGA_A3.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/Demo2_FPGA_B0.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/Demo2_FPGA_B0.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/Demo2_FPGA_B2.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/Demo2_FPGA_B2.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/Demo2_FPGA_B3.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/Demo2_FPGA_B3.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/Demo2_FPGA_B4.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/Demo2_FPGA_B4.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/Demo2_FPGA_B5.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/Demo2_FPGA_B5.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/Demo2x_FPGA_C0.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/Demo2x_FPGA_C0.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/Demo2x_FPGA_C1.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/Demo2x_FPGA_C1.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/HDMI_Demo_00CE.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/HDMI_Demo_00CE.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/HDMI_Demo_C0CE.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/HDMI_Demo_C0CE.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/HSSA_Deser_CC.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/HSSA_Deser_CC.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/HSSA_Deser_CD.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/HSSA_Deser_CD.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/MIDES_FB_FPGA_A0.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/MIDES_FB_FPGA_A0.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/MIDES_FB_FPGA_A1.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/MIDES_FB_FPGA_A1.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/CameraLinkDemo_5551.cdat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/habi/GlobalDiagnostiX/HEAD/aptina/data/board_data/CameraLinkDemo_5551.cdat
--------------------------------------------------------------------------------
/aptina/data/board_data/MI_1007.cfg:
--------------------------------------------------------------------------------
1 | CHIP_FILE = "Demo2_FPGA_*.cdat"
2 | CHIP_FILE = "DualMipiFPGA*.cdat"
3 | CHIP_FILE = "HSSA_Deser_*.cdat"
4 | CHIP_FILE = "HDMI_Demo*.cdat"
5 | CHIP_FILE = "SOC1040_3D_BASEBOARD_*.cdat"
6 |
--------------------------------------------------------------------------------
/aptina/data/board_data/MI_100D.cfg:
--------------------------------------------------------------------------------
1 | CHIP_FILE = "Demo2x_FPGA_*.cdat"
2 | CHIP_FILE = "HSSA_Deser_*.cdat"
3 | CHIP_FILE = "DualMipiFPGA*.cdat"
4 | CHIP_FILE = "HDMI_Demo*.cdat"
5 | CHIP_FILE = "SOC1040_3D_BASEBOARD_*.cdat"
6 |
--------------------------------------------------------------------------------
/elphel/ftp_files_to_camera.sh:
--------------------------------------------------------------------------------
1 | # look for all PHP files in current directory
2 | # for each of those, generate an FTP command to transfer them to the camera
3 | for i in `ls *.php`
4 | do curl -T $i ftp://192.168.0.9/var/html/ --user root:pass
5 | done
--------------------------------------------------------------------------------
/aptina/data/board_data/MI_100E.cfg:
--------------------------------------------------------------------------------
1 | CHIP_FILE = "Demo3*.cdat"
2 | CHIP_FILE = "HSSA_Deser_*.cdat"
3 | CHIP_FILE = "DualMipiFPGA*.cdat"
4 | CHIP_FILE = "HDMI_Demo*.cdat"
5 | CHIP_FILE = "CameraLinkDemo*.cdat"
6 | CHIP_FILE = "SOC1040_3D_BASEBOARD_*.cdat"
7 | CHIP_FILE = "AB1800_FPGA*.cdat"
8 |
--------------------------------------------------------------------------------
/DetectorMockup/README.md:
--------------------------------------------------------------------------------
1 | This folder contains the [OpenSCAD](http://www.openscad.org/) files for the GlobalDiagnostiX detector.
2 |
3 | All *.scad files are the "originals", any *.stl files are exported from those and can be viewed on GitHub.
4 |
5 | Use [this configuration](http://www.thingiverse.com/thing:263620) for easily editing the .scad files in [Geany](http://geany.org/).
6 |
--------------------------------------------------------------------------------
/Analysis/README.md:
--------------------------------------------------------------------------------
1 | This folder of the GlobalDiagnostiX repository contains Python scripts to work with the data acquired during the Master Thesis of a student of the Bern University of Applied Sciences (MAS MedTec).
2 |
3 | He was taking radiographies (in total more than 4000) of different combinations of Scintillators, CMOS sensors and lenses.
4 | The scripts deal with processing, archiving and visualizing the data.
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ImageJ-Elphel/
2 | *.pdf
3 | *.png
4 | *.pyc
5 | matplotlib2tikz*
6 | libtiff*
7 | Images
8 | ImageJ-Elphel
9 | elphel/snapfull.php
10 | elphel/parsedit.php
11 | elphel/setparameters*.php
12 | tiscamera/gige/
13 | tiscamera/gst/
14 | tiscamera/tools/
15 | tiscamera/v4l2/
16 | snapshot_???.jpg
17 | .__a*
18 | ._*
19 | Desktop/*
20 | ImageJ/*
21 | Config
22 | .DS_Store
23 | geany_run_script.sh
24 | test.py
25 | .idea
26 |
--------------------------------------------------------------------------------
/.gitconfig:
--------------------------------------------------------------------------------
1 | [user]
2 | name = David Haberthür
3 | email = email@davidhaberthuer.ch
4 | [core]
5 | editor = nano
6 | [difftool]
7 | prompt = false
8 | [alias]
9 | dt = difftool
10 | l = log --graph --pretty=format:'%C(red)%h%Creset%C(blue)%d%Creset %C(white)%s%Creset %C(white dim)(by %an %ar)%Creset'
11 | ll = !git l --all
12 | [color]
13 | ui=auto
14 | [credential]
15 | helper = cache --timeout=3600
16 | [branch "master"]
17 | mergeoptions = --no-ff
18 |
--------------------------------------------------------------------------------
/GOTTHARD/atte_si2.dat:
--------------------------------------------------------------------------------
1 | 1.00000E-03 1.570E+03
2 | 1.50000E-03 5.355E+02
3 | 1.83890E-03 3.092E+02
4 | 1.83890E-03 3.192E+03
5 | 2.00000E-03 2.777E+03
6 | 3.00000E-03 9.784E+02
7 | 4.00000E-03 4.529E+02
8 | 5.00000E-03 2.450E+02
9 | 6.00000E-03 1.470E+02
10 | 8.00000E-03 6.468E+01
11 | 1.00000E-02 3.389E+01
12 | 1.50000E-02 1.034E+01
13 | 2.00000E-02 4.464E+00
14 | 3.00000E-02 1.436E+00
15 | 4.00000E-02 7.012E-01
16 | 5.00000E-02 4.385E-01
17 | 6.00000E-02 3.207E-01
18 | 8.00000E-02 2.228E-01
19 | 1.00000E-01 1.835E-01
20 |
--------------------------------------------------------------------------------
/PlotBallLenses.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Plot ball lenses
5 | """
6 |
7 | import matplotlib.pylab as plt
8 | import numpy
9 |
10 | Dia = numpy.arange(0, 15, 0.2)
11 | NA = (0.918919 * (-1.0 + Dia)) / Dia
12 | FNo = (0.544118 * Dia) / (-1.0 + Dia)
13 |
14 | plt.plot(Dia, NA, 'r', label='NA')
15 | plt.plot(Dia, FNo, 'g', label='FNo')
16 | plt.legend(loc='best')
17 | plt.xlim([1.5, 10])
18 | plt.ylim([0.3, 1.2])
19 |
20 | for i in (2, 8):
21 | plt.axvline(i, color='k')
22 | if i > 3:
23 | plt.axhline(NA[numpy.where(Dia == i)], color='k')
24 | plt.axhline(FNo[numpy.where(Dia == i)], color='k')
25 |
26 | plt.show()
27 |
--------------------------------------------------------------------------------
/NumericalAperture.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import matplotlib.pylab as plt
4 | import numpy
5 | from scipy import integrate
6 |
7 | lpa = 400.0
8 | hpa = 300.0
9 | pxs = 0.194
10 | maxres = 1.3
11 | epx = 1 / (2 * maxres)
12 |
13 | tsl = 1.0
14 | nxp = 1.0
15 | NAc = pxs / (tsl / 2)
16 | NAa = integrate.quad(lambda x: numpy.arctan(pxs / (2 * x)), 0.01, 1)[0]
17 | print 'middle NA:', NAc
18 | print 'average NA:', NAa
19 |
20 | pcs = 0.006944
21 | mag = 36 / lpa
22 | b = 50.0
23 | g = b / mag
24 |
25 | FStop = 1.4
26 | NAdet = b / (FStop * 2 * g)
27 |
28 | mag = numpy.arange(0, 1.01, 0.01)
29 |
30 | legend = []
31 | plt.figure()
32 | for FStop in [0.5, 0.8, 1, 1.25, 1.4, 2]:
33 | plt.plot(mag, mag / (2 * FStop * (1 + mag)))
34 | legend.append(FStop)
35 | plt.legend(legend)
36 | plt.hlines(NAa, 0, 1)
37 | plt.xlabel('Magnification')
38 | plt.ylabel('NA')
39 | plt.show()
40 |
--------------------------------------------------------------------------------
/Info/OTF.md:
--------------------------------------------------------------------------------
1 | # OTF/MTF calculation for the Elphel Camera
2 |
3 | This does not work on the Raspberry Pi (yet), since I wasn't able to compile the ImageJ plugins from Elphel.
4 |
5 | # On a powerful machine
6 | * get the Elphel ImageJ plugins by issuing `git clone git://elphel.git.sourceforge.net/gitroot/elphel/ImageJ-Elphel ImageJ-Elphel` (maybe add it later to your `.gitignore`)
7 | * Follow the directions on the [Elphel Wiki](http://wiki.elphel.com/index.php?title=Measure_OTF_of_the_lens-sensor_system#B_-_generate) to generate a test pattern.
8 | * Take an image with the stated camera settings (Set them with [this link here](http://192.168.0.9/parsedit.php?embed=0.1&title=OTF+Settings>AB_R>AB_G>AB_GB>AB_B&QUALITY) (use gamma.php (also in the repo to set Gamma to 1.0 and black to 0.0).
9 | * Analyze the image according to the [guidelines given by Elphel]http://wiki.elphel.com/index.php?title=Measure_OTF_of_the_lens-sensor_system#ImageJ)
10 |
--------------------------------------------------------------------------------
/Spectra/Xray-Spectrum_040kV.txt:
--------------------------------------------------------------------------------
1 | # https://w9.siemens.com/cms/oemproducts/Home/X-rayToolbox/spektrum/Pages/radIn.aspx
2 | # Anode material: Tungsten
3 | # Peak tube voltage: 40 kV
4 | # Relative voltage ripple: 0.04
5 | # Air kerma 0.0025 Gy
6 | # Mean energy 30.209266 keV
7 | # List of filters
8 | # Material Thickness, mm
9 | # AIR 1400
10 | # Al 4
11 | 8 0
12 | 9 2.23512e-015
13 | 10 2.18948e-009
14 | 11 4.31922e-006
15 | 12 0.00232188
16 | 13 0.300628
17 | 14 14.5561
18 | 15 298.809
19 | 16 1906.89
20 | 17 8932.95
21 | 18 30532.1
22 | 19 88800
23 | 20 205627
24 | 21 353152
25 | 22 539114
26 | 23 791482
27 | 24 1.06265e+006
28 | 25 1.35991e+006
29 | 26 1.64083e+006
30 | 27 1.9089e+006
31 | 28 2.21045e+006
32 | 29 2.49449e+006
33 | 30 2.78835e+006
34 | 31 2.78741e+006
35 | 32 2.67985e+006
36 | 33 2.50978e+006
37 | 34 2.246e+006
38 | 35 1.88899e+006
39 | 36 1.43309e+006
40 | 37 878742
41 | 38 519053
42 | 39 197429
43 | 40 59656.5
44 | 41 0
45 |
46 |
--------------------------------------------------------------------------------
/RunCalculate.sh:
--------------------------------------------------------------------------------
1 | # kill all runnig fiji jobs
2 | killall fiji-linux;
3 | rm -r Config
4 | # remove all previously calculated images and logfiles
5 | rm /afs/psi.ch/project/EssentialMed/Dev/DetectorConfiguration/*.png
6 | rm /afs/psi.ch/project/EssentialMed/Dev/DetectorConfiguration/*.txt
7 | # calculate some stuff
8 | for s in {3..43..5}; # Field of View/ScreenSize
9 | do echo FOV $s;
10 | for c in {1..10..2}; # Sensor Size
11 | do echo SensorSize $c;
12 | for l in {2..5..1}; # lp/mm
13 | do echo lp_mm $l;
14 | for o in 90; # Opening Angle
15 | do echo OpeningAngle $o;
16 | for n in 1; # NA
17 | do echo NA $n
18 | for f in 1; # FStop
19 | do echo FStop $f
20 | for e in 50; # Energy
21 | do echo Energy $e;
22 | python CalculateDetector.py -s $s -o $o -n $n -f $f -c $c -e $e -l $l -p > /dev/null;
23 | done;
24 | done;
25 | done;
26 | done;
27 | done;
28 | done;
29 | done
30 | # open fiji
31 | /home/scratch/Apps/Fiji.app/fiji-linux -eval 'run("Image Sequence...", "open=/afs/psi.ch/project/EssentialMed/Dev/Config starting=1 increment=1 scale=100 file=png or=[] sort");' & # start fiji
32 |
--------------------------------------------------------------------------------
/Spectra/Xray-Spectrum_046kV.txt:
--------------------------------------------------------------------------------
1 | # https://w9.siemens.com/cms/oemproducts/Home/X-rayToolbox/spektrum/Pages/radIn.aspx
2 | # Anode material: Tungsten
3 | # Peak tube voltage: 46 kV
4 | # Relative voltage ripple: 0.04
5 | # Air kerma 0.0025 Gy
6 | # Mean energy 33.338509 keV
7 | # List of filters
8 | # Material Thickness, mm
9 | # AIR 1400
10 | # Al 4
11 | 8 0
12 | 9 1.98718e-015
13 | 10 1.56485e-009
14 | 11 2.66078e-006
15 | 12 0.00130587
16 | 13 0.16097
17 | 14 8.00576
18 | 15 164.484
19 | 16 1090.91
20 | 17 5210.25
21 | 18 17936.2
22 | 19 52353.3
23 | 20 123040
24 | 21 214001
25 | 22 335073
26 | 23 504194
27 | 24 700618
28 | 25 931312
29 | 26 1.17712e+006
30 | 27 1.44421e+006
31 | 28 1.70858e+006
32 | 29 1.97753e+006
33 | 30 2.24324e+006
34 | 31 2.34505e+006
35 | 32 2.38215e+006
36 | 33 2.39185e+006
37 | 34 2.36321e+006
38 | 35 2.29126e+006
39 | 36 2.16771e+006
40 | 37 1.99989e+006
41 | 38 1.88095e+006
42 | 39 1.73532e+006
43 | 40 1.64187e+006
44 | 41 1.38155e+006
45 | 42 1.07637e+006
46 | 43 777432
47 | 44 430085
48 | 45 103190
49 | 46 0
50 | 47 0
51 |
52 |
--------------------------------------------------------------------------------
/AbsorptionCoefficients.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf8 -*-
2 |
3 | """
4 | Script to plot some absorption coefficients from NIST.
5 | The absorption coefficient data was downloaded as ASCII format table from
6 | http://physics.nist.gov/PhysRefData/XrayMassCoef/tab4.html as "material.dat"
7 | for some materials.
8 | """
9 |
10 | import os
11 | import glob
12 | import matplotlib.pylab as plt
13 | import numpy as np
14 |
15 | BaseDir = os.path.join(os.getcwd(), 'nist', '*')
16 | print 'Found', len(glob.glob(BaseDir)), 'files with data from NIST'
17 | for item in glob.glob(BaseDir):
18 | print 'loading', os.path.basename(item)
19 | # Skip lines in which there's more than the info we need (K, L, M, etc)
20 | # http://stackoverflow.com/a/17151323
21 | with open(item) as f:
22 | lines = (line for line in f if len(line.split()) < 4)
23 | Data = np.loadtxt(lines)
24 | plt.loglog(Data[:, 0], Data[:, 1],
25 | label=os.path.splitext(os.path.basename(item))[0])
26 |
27 | plt.rc('text', usetex=True)
28 | plt.title(r"$\mu/\rho$ [$\textrm{cm}^{2}$/g]")
29 | plt.legend()
30 | plt.show()
31 |
--------------------------------------------------------------------------------
/Info/triggering.md:
--------------------------------------------------------------------------------
1 | Triggering the Elphel Camera
2 | ============================
3 |
4 | Details are on the [Elphel wiki page on triggering](http://wiki.elphel.com/index.php?title=Trigger), nonetheless here are some notes.
5 |
6 | The TRIG_PERIOD is in 32 bit or 96 kHz, meaning that if you set the value to 96000000, this equals to one trigger pulse per second, 48000000 to two pulses per second, obviously.
7 |
8 | To see something on the attached LED (or detect a pulse on an attached cable), you have to se the TRIG_OUT to 800000, and set the TRIG_BITLENGTH sufficiently hight, e.g above 128. If you set TRIG_BITLENGTH to low values, you barely see something on the LED, while setting it to 255 gives a satisfying blink on the LED.
9 |
10 | You'll get the settings below (external trigger, two per second, with a blinking LED) if you click on the [link here](http://192.168.0.9/parsedit.php?embed=0.1&title=External+trigger+controls&TRIG=4&TRIG_PERIOD=96000000&&TRIG_BITLENGTH=255&TRIG_OUT=0x800000). This calls all necessary parameters. Set them with pressing "Apply".
11 |
12 | 
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Development Repository for OmmatiDiag
2 |
3 | [](https://landscape.io/github/habi/GlobalDiagnostiX/master)
4 |
5 | This repository tracks (mostly Python) code written while working on OmmatiDiag, the affordable, reliable and standard-compliant detector for the [GlobalDiagnostiX][GDX]-system.
6 |
7 | [GDX]: http://globaldiagnostix.org
8 |
9 | This repository follow the [PEP 8 -- Style Guide for Python Code][pep8] and the [Git branching model described by Vincent Driessen][branching] as closely as possible.
10 |
11 | [pep8]: http://www.python.org/dev/peps/pep-0008/
12 | [branching]: http://nvie.com/posts/a-successful-git-branching-model/
13 |
14 | Since this repository is part of a scientific project and is depending on a particular set of hardware components, it seems silly to make it closed source, the whole thing is covered by [an (un)license](LICENSE).
15 |
16 | [I, the author][I] of those files am very grateful if you notify me of any [issues](issues), submit pull requests and tell me of (better) ideas on how to implement things and any tips!
17 |
18 | [I]: http://davidhaberthuer.ch/
19 |
--------------------------------------------------------------------------------
/Spectra/Xray-Spectrum_053kV.txt:
--------------------------------------------------------------------------------
1 | # https://w9.siemens.com/cms/oemproducts/Home/X-rayToolbox/spektrum/Pages/radIn.aspx
2 | # Anode material: Tungsten
3 | # Peak tube voltage: 53 kV
4 | # Relative voltage ripple: 0.04
5 | # Air kerma 0.0025 Gy
6 | # Mean energy 36.445385 keV
7 | # List of filters
8 | # Material Thickness, mm
9 | # AIR 1400
10 | # Al 4
11 | # Spektrum
12 | 8 0
13 | 9 1.71595e-015
14 | 10 1.1049e-009
15 | 11 1.71807e-006
16 | 12 0.000809718
17 | 13 0.09614
18 | 14 4.90176
19 | 15 100.745
20 | 16 695.258
21 | 17 3383.67
22 | 18 11694.6
23 | 19 34180.5
24 | 20 81393
25 | 21 143090
26 | 22 228396
27 | 23 349846
28 | 24 497879
29 | 25 678418
30 | 26 881593
31 | 27 1.11381e+006
32 | 28 1.33682e+006
33 | 29 1.57228e+006
34 | 30 1.80097e+006
35 | 31 1.92491e+006
36 | 32 2.00847e+006
37 | 33 2.07976e+006
38 | 34 2.13157e+006
39 | 35 2.16079e+006
40 | 36 2.16104e+006
41 | 37 2.13919e+006
42 | 38 2.11895e+006
43 | 39 2.0783e+006
44 | 40 2.05376e+006
45 | 41 1.94523e+006
46 | 42 1.77942e+006
47 | 43 1.61255e+006
48 | 44 1.47703e+006
49 | 45 1.32117e+006
50 | 46 1.11423e+006
51 | 47 895933
52 | 48 742433
53 | 49 575168
54 | 50 464937
55 | 51 269209
56 | 52 101830
57 | 53 2665.74
58 | 54 0
59 |
60 |
--------------------------------------------------------------------------------
/elphel/elphel.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Quickly grab images from the Elphel camera we have on loan
5 | """
6 |
7 | from optparse import OptionParser
8 | import os
9 | import urllib
10 | import time
11 |
12 | parser = OptionParser()
13 | usage = 'usage: % prog [options] arg'
14 |
15 | parser.add_option('-i', dest='Images', help='how many images should I save?',
16 | metavar='1234', type=int)
17 | (options, args) = parser.parse_args()
18 |
19 | # Make a subdirectory to the current directory we're in
20 | try:
21 | os.mkdir(os.path.join(os.getcwd(), 'Elphel'))
22 | except OSError:
23 | print 'Elphel-directory already exists'
24 | SaveDir = os.path.join(os.getcwd(), 'Elphel', str(time.time()))
25 | os.mkdir(SaveDir)
26 |
27 | # get options.Images number of images as fast as possible from the camera
28 | for i in range(options.Images):
29 | print 'writing image', i, '/', len(range(options.Images))
30 | # get the url of the camera which spit out an image
31 | # save the image to 'SaveDir', with an unique name based on the current
32 | # time
33 | urllib.urlretrieve("http://192.168.0.9:8081/wait/img",
34 | os.path.join(SaveDir, str(time.time()) + '.jpg'))
35 |
36 | print 'saved to', SaveDir
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
4 | software, either in source code form or as a compiled binary, for any purpose,
5 | commercial or non-commercial, and by any means.
6 |
7 | In jurisdictions that recognize copyright laws, the author or authors of this
8 | software dedicate any and all copyright interest in the software to the public
9 | domain. We make this dedication for the benefit of the public at large and to
10 | the detriment of our heirs and successors. We intend this dedication to be an
11 | overt act of relinquishment in perpetuity of all present and future rights to
12 | this software under copyright law.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
18 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
19 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 |
21 | For more information, please refer to
22 |
--------------------------------------------------------------------------------
/Spectra/Xray-Spectrum_060kV.txt:
--------------------------------------------------------------------------------
1 | # https://w9.siemens.com/cms/oemproducts/Home/X-rayToolbox/spektrum/Pages/radIn.aspx
2 | # Anode material: Tungsten
3 | # Peak tube voltage: 60 kV
4 | # Relative voltage ripple: 0.04
5 | # Air kerma 0.0025 Gy
6 | # Mean energy 39.416324 keV
7 | # List of filters
8 | # Material Thickness, mm
9 | # AIR 1400
10 | # Al 4
11 | # Spektrum
12 | 8 0
13 | 9 1.50707e-015
14 | 10 8.10424e-010
15 | 11 1.19912e-006
16 | 12 0.000563206
17 | 13 0.0653169
18 | 14 3.39192
19 | 15 69.6778
20 | 16 497.807
21 | 17 2460.48
22 | 18 8513.24
23 | 19 24869.9
24 | 20 59820.6
25 | 21 105995
26 | 22 171370
27 | 23 265504
28 | 24 383507
29 | 25 530369
30 | 26 699915
31 | 27 898154
32 | 28 1.08904e+006
33 | 29 1.29486e+006
34 | 30 1.49358e+006
35 | 31 1.61521e+006
36 | 32 1.71024e+006
37 | 33 1.79963e+006
38 | 34 1.8754e+006
39 | 35 1.93733e+006
40 | 36 1.98119e+006
41 | 37 2.01207e+006
42 | 38 2.03167e+006
43 | 39 2.03595e+006
44 | 40 2.04195e+006
45 | 41 1.99578e+006
46 | 42 1.89892e+006
47 | 43 1.79921e+006
48 | 44 1.74227e+006
49 | 45 1.6747e+006
50 | 46 1.55661e+006
51 | 47 1.43114e+006
52 | 48 1.35262e+006
53 | 49 1.26459e+006
54 | 50 1.18109e+006
55 | 51 1.05286e+006
56 | 52 932065
57 | 53 805356
58 | 54 674131
59 | 55 546897
60 | 56 380495
61 | 57 243354
62 | 58 126191
63 | 59 31397.8
64 | 60 2498.36
65 | 61 0
66 |
67 |
--------------------------------------------------------------------------------
/aptina/data/board_data/BigDog_SMFPGA_A.cdat:
--------------------------------------------------------------------------------
1 | [CHIP_DESCRIPTOR]
2 | CHIPNAME = "BigDog FPGA"
3 | SERIAL_BASE_ADDRESS = 0x6a
4 | SERIAL_DATA_SIZE = 16
5 | [END]
6 |
7 | //REGDEF = {ADDR, TYPE, MASK, RW, DEFAULT, DESC, DETAIL}
8 | // {BITDEF, MASK, RW, DESC, DETAIL}
9 | // {BITDEF, MASK, RW, DESC, DETAIL}
10 | // ...
11 | // {BITDEF, MASK, RW, DESC, DETAIL}
12 |
13 | [REGISTERS]
14 | CHIP_VERSION_REG = {0x0000, CHIP, 0xFFFF, RO, 0xCAFE, "Frame Buffer_ID", ""}
15 | Column_Count = {0x0001, CHIP, 0xFFFF, RO, 0x0000, "Column Count", ""}
16 | Row_Count = {0x0002, CHIP, 0xFFFF, RO, 0x0000, "Row Count", ""}
17 | Row_Time = {0x0003, CHIP, 0xFFFF, RO, 0x0000, "Row Time", ""}
18 | Frame_Time_Lower = {0x0004, CHIP, 0xFFFF, RO, 0x0000, "LSBs of Frame Time", ""}
19 | Frame_Time_Upper = {0x0005, CHIP, 0xFFFF, RO, 0x0000, "MSBs of Frame Time", ""}
20 | FB_Config = {0x0007, CHIP, 0x000F, RW, 0x0000, "Frame Buffer Config", ""}
21 | {Output_Mode, 0x0003, RW, "0: 8 Bit, 1: 10 Bit, 2: 12 Bit", ""}
22 | {Swizzle, 0x0004, RW, "Swizzle output of Frame Buffer", ""}
23 | {Capture_Posdge, 0x0008, RW, "Capture incoming data on posedge of pixclk", ""}
24 | [END]
--------------------------------------------------------------------------------
/readAptinaRAW.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This script reads the RAW files from the Aptina cameras as numpy arrays,
5 | ready for display or further use.
6 | Made to help Valerie Duay get up to speed :)
7 | """
8 | import os
9 | import numpy
10 | import matplotlib.pyplot as plt
11 |
12 | Directory = '/scratch/tmp/DevWareX/MT9M001/DSL949A-NIR/'
13 | Folder = '1394629994_MT9M001_DSL949A-NIR_0.0_0.0f_040ms_090mm_to150mm'
14 | File = 'MT9M001_1280x1024_DSL949A-NIR_0.0_0.0f_040ms_090mm_to150mm_090mm.raw'
15 | Size = [int(File.split('_')[1].split('x')[1]),
16 | int(File.split('_')[1].split('x')[0])]
17 |
18 | # fromfile
19 | FileToLoad = os.path.join(Directory, Folder, File)
20 |
21 | FromFile = numpy.fromfile(FileToLoad, dtype=numpy.uint16).reshape(Size)
22 | # FromFile -= numpy.mean(FromFile)
23 |
24 | MemMap = numpy.memmap(FileToLoad, dtype=numpy.uint16, shape=(Size[0], Size[1]))
25 | # MemMap -= numpy.mean(MemMap)
26 |
27 | plt.figure(File)
28 | plt.subplot(121)
29 | plt.imshow(FromFile, cmap='gray')
30 | plt.title('numpy.fromfile > leaves file')
31 | plt.subplot(122)
32 | plt.imshow(MemMap, cmap='gray')
33 | plt.title('numpy.memmap > destroys file')
34 | plt.show()
35 |
36 | print 'Only use "numpy.memmap" for displaying files! If you perform some',\
37 | 'calculations on the files (e.g "File -= numpy.mean(File)") these',\
38 | 'calculations are immediately saved to disk, essentially destroying the',\
39 | 'file! In this case use "numpy.fromfile"!'
40 |
--------------------------------------------------------------------------------
/Spectra/Xray-Spectrum_070kV.txt:
--------------------------------------------------------------------------------
1 | # https://w9.siemens.com/cms/oemproducts/Home/X-rayToolbox/spektrum/Pages/radIn.aspx
2 | # Anode material: Tungsten
3 | # Peak tube voltage: 70 kV
4 | # Relative voltage ripple: 0.04
5 | # Air kerma 0.0025 Gy
6 | # Mean energy 43.293309 keV
7 | # List of filters
8 | # Material Thickness, mm
9 | # AIR 1400
10 | # Al 4
11 | # Spektrum
12 | 8 0
13 | 9 1.30561e-015
14 | 10 5.57532e-010
15 | 11 8.00187e-007
16 | 12 0.00038382
17 | 13 0.0436005
18 | 14 2.2998
19 | 15 47.1252
20 | 16 350.865
21 | 17 1764.36
22 | 18 6097.86
23 | 19 17772.4
24 | 20 43204.5
25 | 21 77159.5
26 | 22 126272
27 | 23 197672
28 | 24 289283
29 | 25 405144
30 | 26 541191
31 | 27 702780
32 | 28 861914
33 | 29 1.03683e+006
34 | 30 1.20556e+006
35 | 31 1.31659e+006
36 | 32 1.41223e+006
37 | 33 1.50653e+006
38 | 34 1.58931e+006
39 | 35 1.6637e+006
40 | 36 1.72767e+006
41 | 37 1.78413e+006
42 | 38 1.82699e+006
43 | 39 1.85966e+006
44 | 40 1.88582e+006
45 | 41 1.8769e+006
46 | 42 1.83091e+006
47 | 43 1.78206e+006
48 | 44 1.76541e+006
49 | 45 1.74255e+006
50 | 46 1.68766e+006
51 | 47 1.62805e+006
52 | 48 1.5895e+006
53 | 49 1.54445e+006
54 | 50 1.48764e+006
55 | 51 1.40843e+006
56 | 52 1.33778e+006
57 | 53 1.26313e+006
58 | 54 1.17954e+006
59 | 55 1.09512e+006
60 | 56 1.00002e+006
61 | 57 912985
62 | 58 838323
63 | 59 758760
64 | 60 706220
65 | 61 627661
66 | 62 550930
67 | 63 470241
68 | 64 394452
69 | 65 306910
70 | 66 220188
71 | 67 136545
72 | 68 88767.1
73 | 69 18522.8
74 | 70 0
75 | 71 0
76 |
77 |
--------------------------------------------------------------------------------
/GOTTHARD/Si_Attenuation.dat:
--------------------------------------------------------------------------------
1 | # from http://physics.nist.gov/PhysRefData/XrayMassCoef/ElemTab/z14.html
2 | # Energy μ/ρ μen/ρ
3 | # (MeV) (cm2/g) (cm2/g)
4 | 1.00000E-03 1.570E+03 1.567E+03
5 | 1.50000E-03 5.355E+02 5.331E+02
6 | 1.83890E-03 3.092E+02 3.070E+02
7 | 1.83890E-03 3.192E+03 3.059E+03
8 | 2.00000E-03 2.777E+03 2.669E+03
9 | 3.00000E-03 9.784E+02 9.516E+02
10 | 4.00000E-03 4.529E+02 4.427E+02
11 | 5.00000E-03 2.450E+02 2.400E+02
12 | 6.00000E-03 1.470E+02 1.439E+02
13 | 8.00000E-03 6.468E+01 6.313E+01
14 | 1.00000E-02 3.389E+01 3.289E+01
15 | 1.50000E-02 1.034E+01 9.794E+00
16 | 2.00000E-02 4.464E+00 4.076E+00
17 | 3.00000E-02 1.436E+00 1.164E+00
18 | 4.00000E-02 7.012E-01 4.782E-01
19 | 5.00000E-02 4.385E-01 2.430E-01
20 | 6.00000E-02 3.207E-01 1.434E-01
21 | 8.00000E-02 2.228E-01 6.896E-02
22 | 1.00000E-01 1.835E-01 4.513E-02
23 | 1.50000E-01 1.448E-01 3.086E-02
24 | 2.00000E-01 1.275E-01 2.905E-02
25 | 3.00000E-01 1.082E-01 2.932E-02
26 | 4.00000E-01 9.614E-02 2.968E-02
27 | 5.00000E-01 8.748E-02 2.971E-02
28 | 6.00000E-01 8.077E-02 2.951E-02
29 | 8.00000E-01 7.082E-02 2.875E-02
30 | 1.00000E+00 6.361E-02 2.778E-02
31 | 1.25000E+00 5.688E-02 2.652E-02
32 | 1.50000E+00 5.183E-02 2.535E-02
33 | 2.00000E+00 4.480E-02 2.345E-02
34 | 3.00000E+00 3.678E-02 2.101E-02
35 | 4.00000E+00 3.240E-02 1.963E-02
36 | 5.00000E+00 2.967E-02 1.878E-02
37 | 6.00000E+00 2.788E-02 1.827E-02
38 | 8.00000E+00 2.574E-02 1.773E-02
39 | 1.00000E+01 2.462E-02 1.753E-02
40 | 1.50000E+01 2.352E-02 1.746E-02
41 | 2.00000E+01 2.338E-02 1.757E-02
42 |
43 |
--------------------------------------------------------------------------------
/nist-attenuation-scraper.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Get some data from NIST
5 | """
6 |
7 | from BeautifulSoup import BeautifulSoup
8 | import urllib2
9 | import matplotlib.pylab as plt
10 |
11 | URL = 'http://physics.nist.gov/PhysRefData/XrayMassCoef/ComTab/muscle.html'
12 |
13 | response = urllib2.urlopen(URL)
14 | html = response.read()
15 | soup = BeautifulSoup(html)
16 |
17 | # ascii = soup.find('pre') # extract ASCII formatted table
18 | # for line in ascii:
19 | # print len(str(line).split())
20 |
21 | Energy = []
22 | Mu = []
23 | Muen = []
24 | table = soup.find('table')
25 | for row in table.findAll('tr'):
26 | col = row.findAll('td')
27 | if len(str(col).split()) == 3:
28 | Energy.append(col[0].find(text=True))
29 | Mu.append(col[1].find(text=True))
30 | Muen.append(col[2].find(text=True))
31 | print col[1]
32 |
33 | plt.loglog(Energy, Mu, label='Mu')
34 | plt.loglog(Energy, Muen, label='Muen')
35 | plt.title(soup.title(text=True))
36 | plt.legend()
37 | plt.show()
38 |
39 | URL = 'http://physics.nist.gov/PhysRefData/XrayMassCoef/ComTab/bone.html'
40 | response = urllib2.urlopen(URL)
41 | html = response.read()
42 | soup = BeautifulSoup(html)
43 |
44 | Energy = []
45 | Mu = []
46 | Muen = []
47 | table = soup.find('table')
48 | for row in table.findAll('tr'):
49 | col = row.findAll('td')
50 | if len(str(col).split()) == 3:
51 | Energy.append(col[0].find(text=True))
52 | Mu.append(col[1].find(text=True))
53 | Muen.append(col[2].find(text=True))
54 | asdf = col[1](text=True)
55 | print type(unicode.join(u'\n', map(unicode, asdf)))
56 |
--------------------------------------------------------------------------------
/nist/water.dat:
--------------------------------------------------------------------------------
1 | #http://physics.nist.gov/PhysRefData/XrayMassCoef/ComTab/water.html
2 | #Water, Liquid
3 | #_________________________________
4 | #
5 | # Energy μ/ρ μen/ρ
6 | # (MeV) (cm2/g) (cm2/g)
7 | #_________________________________
8 |
9 | 1.00000E-03 4.078E+03 4.065E+03
10 | 1.50000E-03 1.376E+03 1.372E+03
11 | 2.00000E-03 6.173E+02 6.152E+02
12 | 3.00000E-03 1.929E+02 1.917E+02
13 | 4.00000E-03 8.278E+01 8.191E+01
14 | 5.00000E-03 4.258E+01 4.188E+01
15 | 6.00000E-03 2.464E+01 2.405E+01
16 | 8.00000E-03 1.037E+01 9.915E+00
17 | 1.00000E-02 5.329E+00 4.944E+00
18 | 1.50000E-02 1.673E+00 1.374E+00
19 | 2.00000E-02 8.096E-01 5.503E-01
20 | 3.00000E-02 3.756E-01 1.557E-01
21 | 4.00000E-02 2.683E-01 6.947E-02
22 | 5.00000E-02 2.269E-01 4.223E-02
23 | 6.00000E-02 2.059E-01 3.190E-02
24 | 8.00000E-02 1.837E-01 2.597E-02
25 | 1.00000E-01 1.707E-01 2.546E-02
26 | 1.50000E-01 1.505E-01 2.764E-02
27 | 2.00000E-01 1.370E-01 2.967E-02
28 | 3.00000E-01 1.186E-01 3.192E-02
29 | 4.00000E-01 1.061E-01 3.279E-02
30 | 5.00000E-01 9.687E-02 3.299E-02
31 | 6.00000E-01 8.956E-02 3.284E-02
32 | 8.00000E-01 7.865E-02 3.206E-02
33 | 1.00000E+00 7.072E-02 3.103E-02
34 | 1.25000E+00 6.323E-02 2.965E-02
35 | 1.50000E+00 5.754E-02 2.833E-02
36 | 2.00000E+00 4.942E-02 2.608E-02
37 | 3.00000E+00 3.969E-02 2.281E-02
38 | 4.00000E+00 3.403E-02 2.066E-02
39 | 5.00000E+00 3.031E-02 1.915E-02
40 | 6.00000E+00 2.770E-02 1.806E-02
41 | 8.00000E+00 2.429E-02 1.658E-02
42 | 1.00000E+01 2.219E-02 1.566E-02
43 | 1.50000E+01 1.941E-02 1.441E-02
44 | 2.00000E+01 1.813E-02 1.382E-02
45 |
--------------------------------------------------------------------------------
/Spectra/Xray-Spectrum_080kV.txt:
--------------------------------------------------------------------------------
1 | # https://w9.siemens.com/cms/oemproducts/Home/X-rayToolbox/spektrum/Pages/radIn.aspx
2 | # Anode material: Tungsten
3 | # Peak tube voltage: 80 kV
4 | # Relative voltage ripple: 0.04
5 | # Air kerma 0.0025 Gy
6 | # Mean energy 47.090489 keV
7 | # List of filters
8 | # Material Thickness, mm
9 | # AIR 1400
10 | # Al 4
11 | # Spektrum
12 | 8 0
13 | 9 1.15282e-015
14 | 10 4.07933e-010
15 | 11 5.90006e-007
16 | 12 0.000287072
17 | 13 0.0320615
18 | 14 1.69769
19 | 15 34.6186
20 | 16 266.128
21 | 17 1354.86
22 | 18 4671.37
23 | 19 13573.8
24 | 20 33239.6
25 | 21 59667.2
26 | 22 98457.8
27 | 23 155176
28 | 24 228901
29 | 25 322980
30 | 26 434231
31 | 27 567370
32 | 28 702432
33 | 29 852895
34 | 30 998523
35 | 31 1.09741e+006
36 | 32 1.18737e+006
37 | 33 1.27797e+006
38 | 34 1.35748e+006
39 | 35 1.43118e+006
40 | 36 1.49819e+006
41 | 37 1.5601e+006
42 | 38 1.61107e+006
43 | 39 1.65538e+006
44 | 40 1.69029e+006
45 | 41 1.69651e+006
46 | 42 1.67794e+006
47 | 43 1.65719e+006
48 | 44 1.65342e+006
49 | 45 1.64456e+006
50 | 46 1.62203e+006
51 | 47 1.59615e+006
52 | 48 1.57191e+006
53 | 49 1.54281e+006
54 | 50 1.50474e+006
55 | 51 1.45231e+006
56 | 52 1.40578e+006
57 | 53 1.35627e+006
58 | 54 1.30303e+006
59 | 55 1.24793e+006
60 | 56 1.27831e+006
61 | 57 1.31212e+006
62 | 58 1.34017e+006
63 | 59 1.36384e+006
64 | 60 1.16978e+006
65 | 61 957791
66 | 62 889066
67 | 63 817347
68 | 64 767765
69 | 65 711040
70 | 66 694086
71 | 67 678689
72 | 68 610181
73 | 69 516132
74 | 70 433197
75 | 71 351428
76 | 72 307603
77 | 73 256206
78 | 74 212459
79 | 75 174017
80 | 76 129644
81 | 77 79640.5
82 | 78 40233
83 | 79 3443.04
84 | 80 36.8706
85 | 81 0
86 |
87 |
--------------------------------------------------------------------------------
/elphel/gamma.php:
--------------------------------------------------------------------------------
1 | "",
12 | "GTAB_G__0816"=>"",
13 | "GTAB_GB__0816"=>"",
14 | "GTAB_B__0816"=>"",
15 | "GTAB_R__0824"=>"",
16 | "GTAB_G__0824"=>"",
17 | "GTAB_GB__0824"=>"",
18 | "GTAB_B__0824"=>""
19 | );
20 | // get existing gamma values
21 | $gammas= elphel_get_P_arr($gammas);
22 | }
23 |
24 | foreach($_GET as $key=>$value) {
25 |
26 | if(strtolower(substr($s, 0, 2))=="0x")
27 | $value= hexdec($value);
28 | switch ($key) {
29 | case "gamma":
30 | // set gammas array and postpone setting values in the camera
31 | $value = intval($value);
32 | $gammas["GTAB_R__0816"]=$value;
33 | $gammas["GTAB_G__0816"]=$value;
34 | $gammas["GTAB_GB__0816"]=$value;
35 | $gammas["GTAB_B__0816"]=$value;
36 | break;
37 | case "black":
38 | // set gammas array and postpone setting values in the camera
39 | $value = intval($value);
40 | $gammas["GTAB_R__0824"]=$value;
41 | $gammas["GTAB_G__0824"]=$value;
42 | $gammas["GTAB_GB__0824"]=$value;
43 | $gammas["GTAB_B__0824"]=$value;
44 | break;
45 | }
46 | // If any gamma value was set, set all gamma values in the camera.
47 | // Was postponed earlier
48 | if ($gammas != NULL) {
49 | // values are taken from the green color
50 | elphel_gamma_add($gammas["GTAB_G__0816"]/100, $gammas["GTAB_G__0824"]);
51 | // Apply the values at the frame ahead
52 | elphel_set_P_arr($gammas, elphel_get_frame() + $ahead);
53 | }
54 | //
55 | // Add code to form the output here
56 | }
57 |
58 | ?>
59 |
--------------------------------------------------------------------------------
/Spectra/Xray-Spectrum_090kV.txt:
--------------------------------------------------------------------------------
1 | # https://w9.siemens.com/cms/oemproducts/Home/X-rayToolbox/spektrum/Pages/radIn.aspx
2 | # Anode material: Tungsten
3 | # Peak tube voltage: 90 kV
4 | # Relative voltage ripple: 0.04
5 | # Air kerma 0.0025 Gy
6 | # Mean energy 50.417544 keV
7 | # List of filters
8 | # Material Thickness, mm
9 | # AIR 1400
10 | # Al 4
11 | # Spektrum
12 | 8 0
13 | 9 1.04158e-015
14 | 10 3.25985e-010
15 | 11 4.90597e-007
16 | 12 0.000232997
17 | 13 0.0254407
18 | 14 1.33779
19 | 15 27.0774
20 | 16 212.969
21 | 17 1092.9
22 | 18 3759.25
23 | 19 10892.2
24 | 20 26798.2
25 | 21 48249
26 | 22 80140.6
27 | 23 126959
28 | 24 188264
29 | 25 266933
30 | 26 360215
31 | 27 472296
32 | 28 589508
33 | 29 721420
34 | 30 849850
35 | 31 938550
36 | 32 1.02206e+006
37 | 33 1.1072e+006
38 | 34 1.18153e+006
39 | 35 1.25149e+006
40 | 36 1.31653e+006
41 | 37 1.37769e+006
42 | 38 1.43107e+006
43 | 39 1.48015e+006
44 | 40 1.51892e+006
45 | 41 1.53174e+006
46 | 42 1.52896e+006
47 | 43 1.52469e+006
48 | 44 1.52522e+006
49 | 45 1.52087e+006
50 | 46 1.51583e+006
51 | 47 1.50822e+006
52 | 48 1.48987e+006
53 | 49 1.46767e+006
54 | 50 1.44256e+006
55 | 51 1.406e+006
56 | 52 1.37255e+006
57 | 53 1.33672e+006
58 | 54 1.30298e+006
59 | 55 1.26746e+006
60 | 56 1.41765e+006
61 | 57 1.57177e+006
62 | 58 1.70271e+006
63 | 59 1.83169e+006
64 | 60 1.4718e+006
65 | 61 1.09428e+006
66 | 62 1.01896e+006
67 | 63 941111
68 | 64 905006
69 | 65 863715
70 | 66 934855
71 | 67 1.00746e+006
72 | 68 881393
73 | 69 740760
74 | 70 646781
75 | 71 549383
76 | 72 518148
77 | 73 482734
78 | 74 453311
79 | 75 428481
80 | 76 396087
81 | 77 359411
82 | 78 328735
83 | 79 282108
84 | 80 269220
85 | 81 236906
86 | 82 197073
87 | 83 167107
88 | 84 137233
89 | 85 108146
90 | 86 78656.5
91 | 87 49455.1
92 | 88 26223.6
93 | 89 8911.82
94 | 90 1620.84
95 | 91 0
96 |
97 |
--------------------------------------------------------------------------------
/elphel/GPIO_input.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Script to work with the Input/Output Pins of the RPi, ltimately thought to
5 | trigger the Elphel camera.
6 | Based on http://code.google.com/p/raspberry-gpio-python/
7 | """
8 |
9 | import sys
10 | import time
11 | # Try to import the GPIO library
12 | try:
13 | import RPi.GPIO as GPIO
14 | except ImportError:
15 | print 'I cannot import RPI.GPIO, you have to run the script as root'
16 | print 'try running it again with'
17 | print '---'
18 | print 'sudo', ' '.join(sys.argv)
19 | print '---'
20 | sys.exit(1)
21 |
22 |
23 | def is_even(i):
24 | return (i % 2) == 0
25 |
26 | # to use Raspberry Pi board pin numbers
27 | # Named sequentially, as seen on the connector. compare
28 | # http://elinux.org/File:GPIOs.png
29 | GPIO.setmode(GPIO.BOARD)
30 | # Named GPIO*, see table http://is.gd/xWDsp7 (e.g. 007 is the last pin)
31 | # GPIO.setmode(GPIO.BCM)
32 |
33 | print 'set up GPIO input channel'
34 | Pin = 26 # BOARD
35 | # Pin = 007 # BMC
36 | GPIO.setup(Pin, GPIO.IN)
37 |
38 | print
39 | print 'I am waiting for you to connect pin', Pin, 'and ground'
40 | print
41 |
42 | # Wait for Input, then print something and wait for a short while
43 | # Code according to http://is.gd/G88UyN
44 | counter = 1
45 | Previous_Reading = 0
46 | while True:
47 | if GPIO.input(Pin):
48 | print "Pin", Pin, "and Ground are connected (" + str(counter),\
49 | "times)."
50 | counter += 1
51 | time.sleep(0.05)
52 |
53 | Counter = 1
54 | Previous_Input = 0
55 | while Counter < 100:
56 | Input = GPIO.input(Pin)
57 | if not Previous_Input and Input:
58 | print "Pin", Pin, "and Ground are connected (" + str(Counter),\
59 | "times)."
60 | Counter += 1
61 | Previous_Input = Input
62 | time.sleep(0.1)
63 |
64 | # Reset every channel that has been set up by this program to INPUT with no
65 | # pullup/pulldown and no event detection.
66 | GPIO.cleanup()
67 |
--------------------------------------------------------------------------------
/nist/a150.dat:
--------------------------------------------------------------------------------
1 | #http://physics.nist.gov/PhysRefData/XrayMassCoef/ComTab/a150.html
2 | #A-150 Tissue-Equivalent Plastic
3 | #________________________________________
4 | #
5 | # Energy μ/ρ μen/ρ
6 | # (MeV) (cm2/g) (cm2/g)
7 | #________________________________________
8 |
9 | 1.00000E-03 2.259E+03 2.256E+03
10 | 1.50000E-03 7.282E+02 7.267E+02
11 | 2.00000E-03 3.183E+02 3.172E+02
12 | 3.00000E-03 9.652E+01 9.576E+01
13 | 4.00000E-03 4.081E+01 4.021E+01
14 | 4.03810E-03 3.966E+01 3.907E+01
15 | 20 K 4.03810E-03 5.629E+01 5.326E+01
16 | 5.00000E-03 3.069E+01 2.901E+01
17 | 6.00000E-03 1.813E+01 1.708E+01
18 | 8.00000E-03 7.914E+00 7.337E+00
19 | 1.00000E-02 4.186E+00 3.771E+00
20 | 1.50000E-02 1.394E+00 1.106E+00
21 | 2.00000E-02 7.068E-01 4.605E-01
22 | 3.00000E-02 3.481E-01 1.378E-01
23 | 4.00000E-02 2.562E-01 6.411E-02
24 | 5.00000E-02 2.198E-01 4.018E-02
25 | 6.00000E-02 2.008E-01 3.095E-02
26 | 8.00000E-02 1.803E-01 2.561E-02
27 | 1.00000E-01 1.680E-01 2.520E-02
28 | 1.50000E-01 1.485E-01 2.736E-02
29 | 2.00000E-01 1.353E-01 2.937E-02
30 | 3.00000E-01 1.173E-01 3.159E-02
31 | 4.00000E-01 1.049E-01 3.245E-02
32 | 5.00000E-01 9.579E-02 3.265E-02
33 | 6.00000E-01 8.857E-02 3.249E-02
34 | 8.00000E-01 7.777E-02 3.172E-02
35 | 1.00000E+00 6.992E-02 3.070E-02
36 | 1.25000E+00 6.253E-02 2.934E-02
37 | 1.50000E+00 5.691E-02 2.805E-02
38 | 2.00000E+00 4.880E-02 2.578E-02
39 | 3.00000E+00 3.907E-02 2.247E-02
40 | 4.00000E+00 3.335E-02 2.025E-02
41 | 5.00000E+00 2.958E-02 1.867E-02
42 | 6.00000E+00 2.690E-02 1.751E-02
43 | 8.00000E+00 2.338E-02 1.592E-02
44 | 1.00000E+01 2.117E-02 1.489E-02
45 | 1.50000E+01 1.819E-02 1.346E-02
46 | 2.00000E+01 1.675E-02 1.275E-02
47 |
--------------------------------------------------------------------------------
/elphel/setexposure.php:
--------------------------------------------------------------------------------
1 | $value) {
11 | $parameters[$key] = convert($value);
12 | }
13 |
14 | // parameters are set X frames in the future
15 | if (isset($_GET['framedelay']))
16 | {
17 | $framedelay = $_GET['framedelay'];
18 | }
19 | else
20 | $framedelay = 3; // default framedelay is 3
21 |
22 | function convert($s) {
23 | // clean up
24 | $s = trim($s, "\" ");
25 | // check if value is in HEX
26 | if(strtoupper(substr($s, 0, 2))=="0X")
27 | return intval(hexdec($s));
28 | else
29 | return intval($s);
30 | }
31 |
32 | // Save CameraIP/phpfile into variable for re-use
33 | $url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'];
34 |
35 | // Give out some HTML code, so that we actually have a page to look at
36 | echo "\n
\n GlobalDiagnostiX - PHP\n\n\n";
37 | echo "GlobalDiagnostiX exposure settings page
\n";
38 |
39 | // only show parameters if we are actually setting anything, i.e when $parameters is not empty
40 | if ( !empty( $parameters ) )
41 | {
42 | echo "The setting parameters from the URL are \n"; print_r($parameters); echo "
\n";
43 | }
44 | echo "
\n";
45 |
46 | if ( isset ( $parameters["exposure"] ) )
47 | {
48 | elphel_set_P_value(ELPHEL_AUTOEXP_ON,0); // turn off autoexposure if we're setting the exposure manually.
49 | elphel_set_P_value(ELPHEL_EXPOS,($parameters['exposure'] * 1000 )); // input is in msec, set is in usec
50 | }
51 |
52 | // wait for at least three frames for the setting from above to stick
53 |
54 | elphel_skip_frames($framedelay);
55 |
56 | echo "Frame ".elphel_get_frame()." has an exposure time of ".(elphel_get_P_value(ELPHEL_EXPOS) / 1000)." msec";
57 |
58 | ?>
59 |
--------------------------------------------------------------------------------
/randomMTF.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Calculate the modulation transfer function of a random image.
5 | Testing the idea described in Daniels1995, http://dx.doi.org/10.1117/12.190433
6 | """
7 |
8 | from scipy import ndimage
9 | import numpy
10 | import matplotlib.pyplot as plt
11 |
12 |
13 | def MTF(ImageBeforeTransformation, ImageAfterTransformation):
14 | # calculate power spectral density of both images, according to Daniels1995
15 | PSD_A = numpy.abs(numpy.fft.fft2(ImageBeforeTransformation)) ** 2
16 | PSD_A = numpy.mean(PSD_A, axis=0)
17 | PSD_B = numpy.abs(numpy.fft.fft2(ImageAfterTransformation)) ** 2
18 | PSD_B = numpy.mean(PSD_B, axis=0)
19 | ImgWidth = ImageBeforeTransformation.shape[1]
20 | aemmteeaeff = numpy.sqrt(PSD_B / PSD_A)[:ImgWidth / 2]
21 | return aemmteeaeff
22 |
23 | length = 1116
24 | RandomImage = numpy.random.randint(2, size=[length, length]) * (2 ** 16)
25 | # Get rid of DC component
26 | RandomImage -= numpy.mean(RandomImage)
27 | RandomImageGauss = ndimage.gaussian_filter(RandomImage, 0.8)
28 |
29 | # PSD according to Daniels1995
30 | PSDImage = numpy.abs(numpy.fft.fft2(RandomImage)) ** 2
31 | PSD = numpy.mean(PSDImage, axis=0)
32 |
33 | PSDImageGauss = numpy.abs(numpy.fft.fft2(RandomImageGauss)) ** 2
34 | PSDGauss = numpy.mean(PSDImageGauss, axis=0)
35 |
36 | plt.subplot(231)
37 | plt.imshow(RandomImage, interpolation='none', cmap='gray')
38 | plt.title('Random image')
39 | plt.subplot(232)
40 | plt.imshow(numpy.fft.fftshift(PSDImage), interpolation='none', cmap='gray')
41 | plt.title('2D FFT')
42 |
43 | plt.subplot(234)
44 | plt.imshow(RandomImageGauss, interpolation='none', cmap='gray')
45 | plt.subplot(235)
46 | plt.imshow(numpy.fft.fftshift(PSDImageGauss), interpolation='none',
47 | cmap='gray')
48 |
49 |
50 | plt.subplot(133)
51 | plt.plot(PSD, label='PSD')
52 | plt.plot(PSDGauss, label='PSD gauss')
53 | plt.xlim([0, length])
54 | plt.legend(loc='best')
55 | plt.title('PSD')
56 |
57 | plt.figure()
58 | plt.plot(MTF(RandomImage, RandomImageGauss))
59 | plt.ylim([0, 1])
60 | plt.title('MTF')
61 |
62 | plt.show()
63 |
--------------------------------------------------------------------------------
/DepthOfFocus.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Script to calculate the depth of focus of the GDX setup.
5 | Formulas from \cite{Greenleaf1950} found via
6 | http://www.dofmaster.com/equations.html
7 | """
8 |
9 | # H is the hyperfocal distance, mm
10 | # f is the lens focal length, mm
11 | # s is the focus distance
12 | # Dn is the near distance for acceptable sharpness
13 | # Df is the far distance for acceptable sharpness
14 | # N is the f-number
15 | # c is the circle of confusion, mm
16 |
17 | f = 3
18 | N = 1.4
19 | c = 0.08
20 | s = 200
21 |
22 | H = f ** 2 / (N * c + f)
23 | NearDistance = (s * (H - f)) / (H + s)
24 | FarDistance = (s * (H - f)) / (H - s)
25 |
26 | print 'The hyperfocal distance is %s' % H
27 | print 'The near distance for acceptable sharpness Dn is %s' % NearDistance
28 | print 'The far distance for acceptable sharpness Df is %s' % FarDistance
29 |
30 |
31 | print 'Also check image at', \
32 | 'http://www.cambridgeincolour.com/tutorials/dof-calculator.htm'
33 |
34 | # Testing some stuff
35 | import matplotlib.pyplot as plt
36 | import os
37 | import numpy
38 |
39 | Experiment = '/afs/psi.ch/project/EssentialMed/MasterArbeitBFH/XrayImages/' +\
40 | 'Toshiba/AR0132/TIS-TBL-6C-3MP/Hand/'
41 | ExperimentID = '5808439'
42 | OriginalImage = plt.imread(os.path.join(Experiment, ExperimentID +
43 | '.image.corrected.png'))
44 | StretchedImage = plt.imread(os.path.join(Experiment, ExperimentID +
45 | '.image.corrected.stretched.png'))
46 |
47 | print 'The minimum of the original image is %s, the maximum is %s' % (
48 | numpy.min(OriginalImage), numpy.max(OriginalImage))
49 | print 'The minimum of the stretched image is %s, the maximum is %s' % (
50 | numpy.min(StretchedImage), numpy.max(StretchedImage))
51 |
52 | plt.figure()
53 | plt.subplot(121)
54 | plt.imshow(OriginalImage, cmap='bone')
55 | plt.subplot(122)
56 | plt.hist(OriginalImage.flatten(), 64)
57 |
58 | plt.figure()
59 | plt.subplot(121)
60 | plt.imshow(StretchedImage, cmap='bone')
61 | plt.subplot(122)
62 | plt.hist(StretchedImage.flatten(), 64)
63 | plt.show()
64 |
--------------------------------------------------------------------------------
/Spectra/Xray-Spectrum_100kV.txt:
--------------------------------------------------------------------------------
1 | # https://w9.siemens.com/cms/oemproducts/Home/X-rayToolbox/spektrum/Pages/radIn.aspx
2 | # Anode material: Tungsten
3 | # Peak tube voltage: 100 kV
4 | # Relative voltage ripple: 0.04
5 | # Air kerma 0.0025 Gy
6 | # Mean energy 53.313420 keV
7 | # List of filters
8 | # Material Thickness, mm
9 | # AIR 1400
10 | # Al 4
11 | # Spektrum
12 | 8 0
13 | 9 9.59328e-016
14 | 10 2.89124e-010
15 | 11 4.6091e-007
16 | 12 0.000202931
17 | 13 0.021419
18 | 14 1.10787
19 | 15 22.1977
20 | 16 176.993
21 | 17 911.805
22 | 18 3131.74
23 | 19 9054.92
24 | 20 22331.4
25 | 21 40256.7
26 | 22 67276.3
27 | 23 107079
28 | 24 159401
29 | 25 226809
30 | 26 306819
31 | 27 403189
32 | 28 506818
33 | 29 624379
34 | 30 739733
35 | 31 820423
36 | 32 897958
37 | 33 977661
38 | 34 1.04709e+006
39 | 35 1.11313e+006
40 | 36 1.1749e+006
41 | 37 1.23355e+006
42 | 38 1.28694e+006
43 | 39 1.33757e+006
44 | 40 1.37779e+006
45 | 41 1.39361e+006
46 | 42 1.40025e+006
47 | 43 1.40595e+006
48 | 44 1.40842e+006
49 | 45 1.40611e+006
50 | 46 1.41058e+006
51 | 47 1.41294e+006
52 | 48 1.39795e+006
53 | 49 1.37977e+006
54 | 50 1.36366e+006
55 | 51 1.33713e+006
56 | 52 1.31163e+006
57 | 53 1.28416e+006
58 | 54 1.26297e+006
59 | 55 1.24035e+006
60 | 56 1.49301e+006
61 | 57 1.75154e+006
62 | 58 1.97258e+006
63 | 59 2.19431e+006
64 | 60 1.68213e+006
65 | 61 1.15031e+006
66 | 62 1.06437e+006
67 | 63 976154
68 | 64 947196
69 | 65 913981
70 | 66 1.06567e+006
71 | 67 1.2192e+006
72 | 68 1.0353e+006
73 | 69 840445
74 | 70 737295
75 | 71 630842
76 | 72 602263
77 | 73 570544
78 | 74 550413
79 | 75 533653
80 | 76 508713
81 | 77 480272
82 | 78 459239
83 | 79 425881
84 | 80 412101
85 | 81 382424
86 | 82 355633
87 | 83 336578
88 | 84 310491
89 | 85 284486
90 | 86 266318
91 | 87 248068
92 | 88 223712
93 | 89 199272
94 | 90 179855
95 | 91 160656
96 | 92 133933
97 | 93 111879
98 | 94 91799.4
99 | 95 71543.4
100 | 96 52299
101 | 97 33826.4
102 | 98 17335.2
103 | 99 5215.23
104 | 100 549.676
105 | 101 0
106 |
107 |
--------------------------------------------------------------------------------
/tiscamera/setup.md:
--------------------------------------------------------------------------------
1 | # Installation
2 | I bought a 5MP USB 2.0 board camera ([DMx 72BUC02](http://www.theimagingsource.com/en_US/products/oem-cameras/usb-cmos-mono/dmm72buc02ml/))
3 | from the Imaging source, to test for the GlobalDiagnostix project.
4 |
5 | To make the Imaging science camera work on the RPI, I had to follow the setup
6 | procedure as stated on the [tiscamera page](http://code.google.com/p/tiscamera/wiki/GettingStartedCMOSUVC),
7 | where the Imaging Source provides the source code to work with their cameras on
8 | Linux.
9 |
10 | I downloaded the code with
11 | > git clone https://code.google.com/p/tiscamera/
12 |
13 | (into /afs/psi.ch/project/EssentialMed/Dev/tiscamera) but could not compile the
14 | code due to missing libraries.
15 |
16 | I had to install `libusb` and the `glib` libraries to make compilation work (and
17 | at the same time installed `mplayer` for looking at the video stream)
18 | This was done (on the Raspberry Pi) with
19 | > sudo apt-get install libglib2.0-dev libusb-dev mplayer
20 |
21 | and with the following command on SL6
22 | > sudo yum install libglib* libusb* mplayer
23 |
24 | Afterwards I did this
25 | > cd tiscamera/tools/euvccam-fw/
26 | > make
27 |
28 | plugged in the camera
29 | > sudo ./euvccam-fw -p
30 |
31 | looked at the output of this command, which informed me that the camera is there
32 | and can be seen by Raspbian.
33 |
34 | To actually look at the image I started `mplayer` with the command below:
35 | > mplayer tv:// -tv driver=v4l2:device=/dev/video0
36 |
37 | This gives a 640x480 window (top left) of the total chip (and made me question
38 | my screwdriver skills, because I suspected that I attached the lens holder
39 | completely wrong).
40 | So, to see the whole chip, start mplayer like this
41 | > mplayer tv:// -tv driver=v4l2:width=2592:height=1922:device=/dev/video0·
42 |
43 | To save a screenshot of the current image, start mplayer with the -vt option,
44 | and press `s` while the image shows (or `S` for continuous, info from [this
45 | website](https://lorenzod8n.wordpress.com/2007/05/23/screenshots-with-mplayer/).
46 | > mplayer tv:// -tv driver=v4l2:device=/dev/video0 -vf screenshot
47 |
--------------------------------------------------------------------------------
/Analysis/UnpackFromArchive.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Once the analysis of a folder has been done, we use the TarToArchive.py script
5 | to pack it up and sent to the PSI tape archive, the DarkDeleter.py script is
6 | then used to delete all the unnecessary files.
7 |
8 | If you want to get some stuff back, it's annoying manual work to go in each
9 | folder and unpack the tar files.
10 |
11 | This script should alleviate this problem.
12 | """
13 |
14 | import os
15 | import subprocess
16 | import fnmatch
17 | import glob
18 |
19 | RootFolder = ('/afs/psi.ch/project/EssentialMed/MasterArbeitBFH/' +
20 | 'XrayImages')
21 |
22 | ListOfTARs = []
23 | for root, dirnames, filenames in os.walk(os.path.join(RootFolder)):
24 | for filename in fnmatch.filter(filenames, '*.gz'):
25 | ListOfTARs.append(os.path.join(root, filename))
26 |
27 | if not ListOfTARs:
28 | print 'Nothing to do!'
29 |
30 | for counter, item in enumerate(ListOfTARs):
31 | print counter, 'of', len(ListOfTARs), '| unpacking', \
32 | item[len(RootFolder) + 1:]
33 | UnpackCommand = ['tar', '-xf', item, '--directory', os.path.dirname(item)]
34 | unpackit = subprocess.Popen(UnpackCommand, stdout=subprocess.PIPE)
35 | output, error = unpackit.communicate()
36 | if output:
37 | print output
38 | if error:
39 | print error
40 | print 'Unpacking done, now removing', item[len(RootFolder) + 1:],
41 | try:
42 | os.remove(item)
43 | except OSError:
44 | print 'It is already gone!'
45 | ExperimentID = os.path.splitext(os.path.splitext(item)[0])[0]
46 | DeletionLog = ExperimentID + '.deletion.log'
47 | print 'as well as', os.path.basename(DeletionLog), \
48 | 'and results from Analyis (all', \
49 | os.path.basename(ExperimentID) + '*.png)'
50 | try:
51 | os.remove(DeletionLog)
52 | for i in glob.glob(ExperimentID + '*.png'):
53 | try:
54 | os.remove(i)
55 | except OSError:
56 | print 'The analysis images are already gone!'
57 | except OSError:
58 | print 'The log file is already gone!'
59 | print 80 * '-'
60 | print 'Done'
61 |
--------------------------------------------------------------------------------
/elphel/GPIO_output.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Script to work with the Input/Output Pins of the RPi, utimately thought to
5 | trigger the Elphel camera.
6 | Based on http://code.google.com/p/raspberry-gpio-python/
7 | """
8 |
9 | import sys
10 | import time
11 | # Try to import the GPIO library
12 | try:
13 | import RPi.GPIO as GPIO
14 | except ImportError:
15 | print 'I cannot import RPI.GPIO, you have to run the script as root'
16 | print 'try running it again with'
17 | print '---'
18 | print 'sudo', ' '.join(sys.argv)
19 | print '---'
20 | sys.exit(1)
21 |
22 | try:
23 | Pin = int(sys.argv[1])
24 | sleepytime = float(sys.argv[2])
25 | steps = int(sys.argv[3])
26 | except IndexError:
27 | print 'Start the script with three parameters'
28 | print sys.argv[0], 'Pin Sleeptime Repeats'
29 | sys.exit(1)
30 |
31 |
32 | def is_even(i):
33 | return (i % 2) == 0
34 |
35 | # to use Raspberry Pi board pin numbers
36 | # Named sequentially, as seen on the connector. compare
37 | # http://elinux.org/File:GPIOs.png
38 | GPIO.setmode(GPIO.BOARD)
39 | # Named GPIO*, see table http://is.gd/xWDsp7 (e.g. 007 is the last pin)
40 | # GPIO.setmode(GPIO.BCM)
41 |
42 | print 'set up GPIO output channel'
43 | # Pin = 26 # BOARD
44 | # Pin = 007 # BMC
45 | GPIO.setup(Pin, GPIO.OUT)
46 |
47 | # set RPi board pin selected above to high for a certain time, wait, set it low
48 | # lather, rinse, repeat for 'steps' steps
49 | try:
50 | for Iteration in range(steps):
51 | if is_even(Iteration):
52 | print str("%.02d" % (Iteration + 1)) + '/' + \
53 | str("%.02d" % (steps)), '| Pin', Pin, '^ for', sleepytime, 's'
54 | GPIO.output(Pin, GPIO.HIGH)
55 | time.sleep(sleepytime)
56 | else:
57 | print str("%.02d" % (Iteration + 1)) + '/' +\
58 | str("%.02d" % (steps)), '| Pin', Pin, 'v for', \
59 | sleepytime, 's'
60 | GPIO.output(Pin, GPIO.LOW)
61 | time.sleep(sleepytime)
62 | except KeyboardInterrupt:
63 | print
64 | print 'User aborted sequence, goodbye'
65 |
66 | # Reset every channel that has been set up by this program to INPUT with no
67 | # pullup/pulldown and no event detection.
68 | GPIO.cleanup()
69 |
--------------------------------------------------------------------------------
/nist/adipose.dat:
--------------------------------------------------------------------------------
1 | #http://physics.nist.gov/PhysRefData/XrayMassCoef/ComTab/adipose.html
2 | #Adipose Tissue (ICRU-44)
3 | #________________________________________
4 | #
5 | # Energy μ/ρ μen/ρ
6 | # (MeV) (cm2/g) (cm2/g)
7 | #________________________________________
8 |
9 | 1.00000E-03 2.628E+03 2.623E+03
10 | 1.03542E-03 2.392E+03 2.387E+03
11 | 1.07210E-03 2.176E+03 2.171E+03
12 | 11 K 1.07210E-03 2.182E+03 2.177E+03
13 | 1.50000E-03 8.622E+02 8.601E+02
14 | 2.00000E-03 3.800E+02 3.787E+02
15 | 2.47200E-03 2.053E+02 2.043E+02
16 | 16 K 2.47200E-03 2.072E+02 2.060E+02
17 | 2.64140E-03 1.707E+02 1.696E+02
18 | 2.82240E-03 1.405E+02 1.396E+02
19 | 17 K 2.82240E-03 1.420E+02 1.409E+02
20 | 3.00000E-03 1.188E+02 1.178E+02
21 | 4.00000E-03 5.054E+01 4.983E+01
22 | 5.00000E-03 2.587E+01 2.531E+01
23 | 6.00000E-03 1.494E+01 1.446E+01
24 | 8.00000E-03 6.300E+00 5.917E+00
25 | 1.00000E-02 3.268E+00 2.935E+00
26 | 1.50000E-02 1.083E+00 8.103E-01
27 | 2.00000E-02 5.677E-01 3.251E-01
28 | 3.00000E-02 3.063E-01 9.495E-02
29 | 4.00000E-02 2.396E-01 4.575E-02
30 | 5.00000E-02 2.123E-01 3.085E-02
31 | 6.00000E-02 1.974E-01 2.567E-02
32 | 8.00000E-02 1.800E-01 2.358E-02
33 | 1.00000E-01 1.688E-01 2.433E-02
34 | 1.50000E-01 1.500E-01 2.737E-02
35 | 2.00000E-01 1.368E-01 2.959E-02
36 | 3.00000E-01 1.187E-01 3.194E-02
37 | 4.00000E-01 1.062E-01 3.283E-02
38 | 5.00000E-01 9.696E-02 3.304E-02
39 | 6.00000E-01 8.965E-02 3.289E-02
40 | 8.00000E-01 7.873E-02 3.211E-02
41 | 1.00000E+00 7.078E-02 3.108E-02
42 | 1.25000E+00 6.330E-02 2.970E-02
43 | 1.50000E+00 5.760E-02 2.839E-02
44 | 2.00000E+00 4.940E-02 2.610E-02
45 | 3.00000E+00 3.955E-02 2.275E-02
46 | 4.00000E+00 3.377E-02 2.050E-02
47 | 5.00000E+00 2.995E-02 1.891E-02
48 | 6.00000E+00 2.725E-02 1.773E-02
49 | 8.00000E+00 2.368E-02 1.612E-02
50 | 1.00000E+01 2.145E-02 1.509E-02
51 | 1.50000E+01 1.843E-02 1.365E-02
52 | 2.00000E+01 1.698E-02 1.293E-02
53 |
--------------------------------------------------------------------------------
/PlotXraySpectra.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Plot the x-ray spectra downloaded from the [Siemens simulator]
5 | (https://w9.siemens.com/cms/oemproducts/Home/X-rayToolbox/spektrum/)
6 | """
7 |
8 | import matplotlib.pylab as plt
9 | import os
10 | import scipy
11 | import numpy as np
12 | from scipy.integrate import trapz
13 |
14 | # http://stackoverflow.com/a/11249430/323100
15 | Spectrapath = '/afs/psi.ch/project/EssentialMed/Dev/Spectra'
16 | Spectra = [
17 | (os.path.join(Spectrapath, 'Xray-Spectrum_040kV.txt')),
18 | (os.path.join(Spectrapath, 'Xray-Spectrum_046kV.txt')),
19 | (os.path.join(Spectrapath, 'Xray-Spectrum_053kV.txt')),
20 | (os.path.join(Spectrapath, 'Xray-Spectrum_060kV.txt')),
21 | (os.path.join(Spectrapath, 'Xray-Spectrum_070kV.txt')),
22 | (os.path.join(Spectrapath, 'Xray-Spectrum_080kV.txt')),
23 | (os.path.join(Spectrapath, 'Xray-Spectrum_090kV.txt')),
24 | (os.path.join(Spectrapath, 'Xray-Spectrum_100kV.txt')),
25 | (os.path.join(Spectrapath, 'Xray-Spectrum_100kV.txt')),
26 | (os.path.join(Spectrapath, 'Xray-Spectrum_120kV.txt'))]
27 |
28 | Data = [(np.loadtxt(FileName)) for FileName in Spectra]
29 | Energy = [int(open(FileName).readlines()[2].split()[4]) for FileName in Spectra]
30 | Mean = [float(open(FileName).readlines()[5].split()[3]) for FileName in Spectra]
31 |
32 | for i in range(len(Spectra)):
33 | plt.plot(Data[i][:, 0], Data[i][:, 1],
34 | label=str(Energy[i]) + 'kV, Mean=' +
35 | str(round(Mean[i], 3)) + 'keV')
36 |
37 | plt.legend(loc='best')
38 | plt.title('X-ray spectra')
39 | plt.xlabel('Energy [kV]')
40 | plt.ylabel('Photons')
41 | plt.savefig('plot.pdf')
42 |
43 | plt.figure(figsize=[22, 5])
44 | for counter, spectra in enumerate(Spectra):
45 | plt.subplot(1, len(Spectra), counter + 1)
46 | plt.plot(Data[counter][:, 0], Data[counter][:, 1])
47 | Integral = scipy.integrate.trapz(Data[counter][:, 1], Data[counter][:, 0])
48 | print 'The integral for', Energy[counter], 'kV is', \
49 | str(round(Integral / 1e6, 3)) + 'e6 photons'
50 | plt.title(str(Energy[counter]) + 'kV\n' +
51 | str(round(Integral / 1e6, 3)) + 'e6 photons')
52 | plt.xlim([0, 150])
53 | plt.ylim([0, 3e6])
54 | # Turn off y-ticks for subplots 2-end (counter >0)
55 | if counter:
56 | plt.setp(plt.gca().get_yticklabels(), visible=False)
57 | plt.tight_layout()
58 | plt.show()
59 |
--------------------------------------------------------------------------------
/Spectra/Xray-Spectrum_120kV.txt:
--------------------------------------------------------------------------------
1 | # https://w9.siemens.com/cms/oemproducts/Home/X-rayToolbox/spektrum/Pages/radIn.aspx
2 | # Anode material: Tungsten
3 | # Peak tube voltage: 120 kV
4 | # Relative voltage ripple: 0.04
5 | # Air kerma 0.0025 Gy
6 | # Mean energy 58.333157 keV
7 | # List of filters
8 | # Material Thickness, mm
9 | # AIR 1400
10 | # Al 4
11 | # Spektrum
12 | 8 0
13 | 9 8.42289e-016
14 | 10 3.01667e-010
15 | 11 5.29287e-007
16 | 12 0.000180995
17 | 13 0.0171807
18 | 14 0.840895
19 | 15 16.3811
20 | 16 130.497
21 | 17 669.317
22 | 18 2304.22
23 | 19 6660.89
24 | 20 16401
25 | 21 29497.8
26 | 22 50004.7
27 | 23 80446.3
28 | 24 120545
29 | 25 172538
30 | 26 234394
31 | 27 309197
32 | 28 392890
33 | 29 488898
34 | 30 585029
35 | 31 654297
36 | 32 721399
37 | 33 791110
38 | 34 852861
39 | 35 912739
40 | 36 967831
41 | 37 1.02086e+006
42 | 38 1.07109e+006
43 | 39 1.12003e+006
44 | 40 1.15928e+006
45 | 41 1.17705e+006
46 | 42 1.19216e+006
47 | 43 1.20689e+006
48 | 44 1.2138e+006
49 | 45 1.21664e+006
50 | 46 1.22735e+006
51 | 47 1.23648e+006
52 | 48 1.22813e+006
53 | 49 1.21744e+006
54 | 50 1.2117e+006
55 | 51 1.19633e+006
56 | 52 1.18048e+006
57 | 53 1.16321e+006
58 | 54 1.15419e+006
59 | 55 1.14432e+006
60 | 56 1.54677e+006
61 | 57 1.95873e+006
62 | 58 2.31496e+006
63 | 59 2.67602e+006
64 | 60 1.933e+006
65 | 61 1.16639e+006
66 | 62 1.06059e+006
67 | 63 952697
68 | 64 929221
69 | 65 902507
70 | 66 1.17044e+006
71 | 67 1.44123e+006
72 | 68 1.18402e+006
73 | 69 917237
74 | 70 784909
75 | 71 651424
76 | 72 623371
77 | 73 592583
78 | 74 583814
79 | 75 576617
80 | 76 560463
81 | 77 541498
82 | 78 529714
83 | 79 510049
84 | 80 497108
85 | 81 474108
86 | 82 471209
87 | 83 455507
88 | 84 437559
89 | 85 419385
90 | 86 409932
91 | 87 400701
92 | 88 383307
93 | 89 365685
94 | 90 351492
95 | 91 336849
96 | 92 321326
97 | 93 304512
98 | 94 291365
99 | 95 278246
100 | 96 267916
101 | 97 257655
102 | 98 244215
103 | 99 230727
104 | 100 220216
105 | 101 209089
106 | 102 195008
107 | 103 179673
108 | 104 167981
109 | 105 156445
110 | 106 142757
111 | 107 129366
112 | 108 117530
113 | 109 104957
114 | 110 92589.9
115 | 111 80799.3
116 | 112 69849.9
117 | 113 60282.2
118 | 114 48043.5
119 | 115 35011.1
120 | 116 24697.7
121 | 117 15567
122 | 118 8464.95
123 | 119 2617.51
124 | 120 541.578
125 | 121 0
126 |
127 |
--------------------------------------------------------------------------------
/nist/lung.dat:
--------------------------------------------------------------------------------
1 | #http://physics.nist.gov/PhysRefData/XrayMassCoef/ComTab/lung.html
2 | # Lung Tissue (ICRU-44)
3 | #________________________________________
4 | #
5 | # Energy μ/ρ μen/ρ
6 | # (MeV) (cm2/g) (cm2/g)
7 | #________________________________________
8 |
9 | 1.00000E-03 3.803E+03 3.791E+03
10 | 1.03542E-03 3.469E+03 3.459E+03
11 | 1.07210E-03 3.164E+03 3.155E+03
12 | 11 K 1.07210E-03 3.176E+03 3.167E+03
13 | 1.50000E-03 1.283E+03 1.280E+03
14 | 2.00000E-03 5.746E+02 5.727E+02
15 | 2.14550E-03 4.707E+02 4.690E+02
16 | 15 K 2.14550E-03 4.752E+02 4.732E+02
17 | 2.30297E-03 3.883E+02 3.865E+02
18 | 2.47200E-03 3.170E+02 3.154E+02
19 | 16 K 2.47200E-03 3.226E+02 3.206E+02
20 | 2.64140E-03 2.668E+02 2.650E+02
21 | 2.82240E-03 2.204E+02 2.189E+02
22 | 17 K 2.82240E-03 2.248E+02 2.229E+02
23 | 3.00000E-03 1.888E+02 1.870E+02
24 | 3.60740E-03 1.103E+02 1.091E+02
25 | 19 K 3.60740E-03 1.125E+02 1.110E+02
26 | 4.00000E-03 8.306E+01 8.181E+01
27 | 5.00000E-03 4.296E+01 4.210E+01
28 | 6.00000E-03 2.497E+01 2.431E+01
29 | 8.00000E-03 1.058E+01 1.010E+01
30 | 1.00000E-02 5.459E+00 5.067E+00
31 | 1.50000E-02 1.721E+00 1.423E+00
32 | 2.00000E-02 8.316E-01 5.740E-01
33 | 3.00000E-02 3.815E-01 1.635E-01
34 | 4.00000E-02 2.699E-01 7.286E-02
35 | 5.00000E-02 2.270E-01 4.393E-02
36 | 6.00000E-02 2.053E-01 3.282E-02
37 | 8.00000E-02 1.826E-01 2.625E-02
38 | 1.00000E-01 1.695E-01 2.550E-02
39 | 1.50000E-01 1.493E-01 2.748E-02
40 | 2.00000E-01 1.359E-01 2.945E-02
41 | 3.00000E-01 1.177E-01 3.167E-02
42 | 4.00000E-01 1.053E-01 3.252E-02
43 | 5.00000E-01 9.607E-02 3.272E-02
44 | 6.00000E-01 8.882E-02 3.257E-02
45 | 8.00000E-01 7.800E-02 3.179E-02
46 | 1.00000E+00 7.013E-02 3.077E-02
47 | 1.25000E+00 6.271E-02 2.940E-02
48 | 1.50000E+00 5.706E-02 2.810E-02
49 | 2.00000E+00 4.900E-02 2.586E-02
50 | 3.00000E+00 3.935E-02 2.262E-02
51 | 4.00000E+00 3.374E-02 2.048E-02
52 | 5.00000E+00 3.005E-02 1.898E-02
53 | 6.00000E+00 2.746E-02 1.789E-02
54 | 8.00000E+00 2.407E-02 1.643E-02
55 | 1.00000E+01 2.198E-02 1.551E-02
56 | 1.50000E+01 1.922E-02 1.427E-02
57 | 2.00000E+01 1.794E-02 1.367E-02
58 |
--------------------------------------------------------------------------------
/nist/muscle.dat:
--------------------------------------------------------------------------------
1 | #http://physics.nist.gov/PhysRefData/XrayMassCoef/ComTab/muscle.html
2 | #Muscle, Skeletal
3 | #________________________________________
4 | #
5 | # Energy μ/ρ μen/ρ
6 | # (MeV) (cm2/g) (cm2/g)
7 | #________________________________________
8 |
9 | 1.00000E-03 3.719E+03 3.709E+03
10 | 1.03542E-03 3.393E+03 3.383E+03
11 | 1.07210E-03 3.094E+03 3.085E+03
12 | 11 K 1.07210E-03 3.100E+03 3.091E+03
13 | 1.50000E-03 1.251E+03 1.247E+03
14 | 2.00000E-03 5.594E+02 5.574E+02
15 | 2.14550E-03 4.581E+02 4.564E+02
16 | 15 K 2.14550E-03 4.626E+02 4.606E+02
17 | 2.30297E-03 3.776E+02 3.762E+02
18 | 2.47200E-03 3.085E+02 3.069E+02
19 | 16 K 2.47200E-03 3.140E+02 3.121E+02
20 | 2.64140E-03 2.597E+02 2.579E+02
21 | 2.82240E-03 2.145E+02 2.130E+02
22 | 17 K 2.82240E-03 2.160E+02 2.143E+02
23 | 3.00000E-03 1.812E+02 1.796E+02
24 | 3.60740E-03 1.057E+02 1.046E+02
25 | 19 K 3.60740E-03 1.100E+02 1.083E+02
26 | 4.00000E-03 8.127E+01 7.992E+01
27 | 5.00000E-03 4.206E+01 4.116E+01
28 | 6.00000E-03 2.446E+01 2.377E+01
29 | 8.00000E-03 1.037E+01 9.888E+00
30 | 1.00000E-02 5.356E+00 4.964E+00
31 | 1.50000E-02 1.693E+00 1.396E+00
32 | 2.00000E-02 8.205E-01 5.638E-01
33 | 3.00000E-02 3.783E-01 1.610E-01
34 | 4.00000E-02 2.685E-01 7.192E-02
35 | 5.00000E-02 2.262E-01 4.349E-02
36 | 6.00000E-02 2.048E-01 3.258E-02
37 | 8.00000E-02 1.823E-01 2.615E-02
38 | 1.00000E-01 1.693E-01 2.544E-02
39 | 1.50000E-01 1.492E-01 2.745E-02
40 | 2.00000E-01 1.358E-01 2.942E-02
41 | 3.00000E-01 1.176E-01 3.164E-02
42 | 4.00000E-01 1.052E-01 3.249E-02
43 | 5.00000E-01 9.598E-02 3.269E-02
44 | 6.00000E-01 8.874E-02 3.254E-02
45 | 8.00000E-01 7.793E-02 3.177E-02
46 | 1.00000E+00 7.007E-02 3.074E-02
47 | 1.25000E+00 6.265E-02 2.938E-02
48 | 1.50000E+00 5.701E-02 2.808E-02
49 | 2.00000E+00 4.896E-02 2.584E-02
50 | 3.00000E+00 3.931E-02 2.259E-02
51 | 4.00000E+00 3.369E-02 2.045E-02
52 | 5.00000E+00 3.000E-02 1.895E-02
53 | 6.00000E+00 2.741E-02 1.786E-02
54 | 8.00000E+00 2.401E-02 1.639E-02
55 | 1.00000E+01 2.192E-02 1.547E-02
56 | 1.50000E+01 1.915E-02 1.421E-02
57 | 2.00000E+01 1.786E-02 1.361E-02
58 |
--------------------------------------------------------------------------------
/nist/bone.dat:
--------------------------------------------------------------------------------
1 | #http://physics.nist.gov/PhysRefData/XrayMassCoef/ComTab/bone.html
2 | #Bone, Cortical (ICRU-44)
3 | #________________________________________
4 | #
5 | # Energy μ/ρ μen/ρ
6 | # (MeV) (cm2/g) (cm2/g)
7 | #________________________________________
8 |
9 | 1.00000E-03 3.781E+03 3.772E+03
10 | 1.03542E-03 3.452E+03 3.444E+03
11 | 1.07210E-03 3.150E+03 3.143E+03
12 | 11 K 1.07210E-03 3.156E+03 3.149E+03
13 | 1.18283E-03 2.434E+03 2.429E+03
14 | 1.30500E-03 1.873E+03 1.869E+03
15 | 12 K 1.30500E-03 1.883E+03 1.878E+03
16 | 1.50000E-03 1.295E+03 1.291E+03
17 | 2.00000E-03 5.869E+02 5.846E+02
18 | 2.14550E-03 4.824E+02 4.803E+02
19 | 15 K 2.14550E-03 7.114E+02 6.961E+02
20 | 2.30297E-03 5.916E+02 5.789E+02
21 | 2.47200E-03 4.907E+02 4.805E+02
22 | 16 K 2.47200E-03 4.962E+02 4.857E+02
23 | 3.00000E-03 2.958E+02 2.897E+02
24 | 4.00000E-03 1.331E+02 1.303E+02
25 | 4.03810E-03 1.296E+02 1.269E+02
26 | 20 K 4.03810E-03 3.332E+02 3.006E+02
27 | 5.00000E-03 1.917E+02 1.757E+02
28 | 6.00000E-03 1.171E+02 1.085E+02
29 | 8.00000E-03 5.323E+01 4.987E+01
30 | 1.00000E-02 2.851E+01 2.680E+01
31 | 1.50000E-02 9.032E+00 8.388E+00
32 | 2.00000E-02 4.001E+00 3.601E+00
33 | 3.00000E-02 1.331E+00 1.070E+00
34 | 4.00000E-02 6.655E-01 4.507E-01
35 | 5.00000E-02 4.242E-01 2.336E-01
36 | 6.00000E-02 3.148E-01 1.400E-01
37 | 8.00000E-02 2.229E-01 6.896E-02
38 | 1.00000E-01 1.855E-01 4.585E-02
39 | 1.50000E-01 1.480E-01 3.183E-02
40 | 2.00000E-01 1.309E-01 3.003E-02
41 | 3.00000E-01 1.113E-01 3.032E-02
42 | 4.00000E-01 9.908E-02 3.069E-02
43 | 5.00000E-01 9.022E-02 3.073E-02
44 | 6.00000E-01 8.332E-02 3.052E-02
45 | 8.00000E-01 7.308E-02 2.973E-02
46 | 1.00000E+00 6.566E-02 2.875E-02
47 | 1.25000E+00 5.871E-02 2.745E-02
48 | 1.50000E+00 5.346E-02 2.623E-02
49 | 2.00000E+00 4.607E-02 2.421E-02
50 | 3.00000E+00 3.745E-02 2.145E-02
51 | 4.00000E+00 3.257E-02 1.975E-02
52 | 5.00000E+00 2.946E-02 1.864E-02
53 | 6.00000E+00 2.734E-02 1.788E-02
54 | 8.00000E+00 2.467E-02 1.695E-02
55 | 1.00000E+01 2.314E-02 1.644E-02
56 | 1.50000E+01 2.132E-02 1.587E-02
57 | 2.00000E+01 2.068E-02 1.568E-02
58 |
--------------------------------------------------------------------------------
/nist/tissue.dat:
--------------------------------------------------------------------------------
1 | #http://physics.nist.gov/PhysRefData/XrayMassCoef/ComTab/tissue.html
2 | #Tissue, Soft (ICRU-44)
3 | #________________________________________
4 | #
5 | # Energy μ/ρ μen/ρ
6 | # (MeV) (cm2/g) (cm2/g)
7 | #________________________________________
8 |
9 | 1.00000E-03 3.712E+03 3.701E+03
10 | 1.03542E-03 3.386E+03 3.376E+03
11 | 1.07210E-03 3.087E+03 3.079E+03
12 | 11 K 1.07210E-03 3.099E+03 3.090E+03
13 | 1.50000E-03 1.251E+03 1.247E+03
14 | 2.00000E-03 5.596E+02 5.577E+02
15 | 2.14550E-03 4.583E+02 4.566E+02
16 | 15 K 2.14550E-03 4.650E+02 4.629E+02
17 | 2.30297E-03 3.800E+02 3.782E+02
18 | 2.47200E-03 3.102E+02 3.086E+02
19 | 16 K 2.47200E-03 3.158E+02 3.137E+02
20 | 2.64140E-03 2.612E+02 2.594E+02
21 | 2.82240E-03 2.158E+02 2.142E+02
22 | 17 K 2.82240E-03 2.188E+02 2.169E+02
23 | 3.00000E-03 1.836E+02 1.819E+02
24 | 3.60740E-03 1.073E+02 1.061E+02
25 | 19 K 3.60740E-03 1.105E+02 1.089E+02
26 | 4.00000E-03 8.161E+01 8.030E+01
27 | 5.00000E-03 4.224E+01 4.135E+01
28 | 6.00000E-03 2.456E+01 2.389E+01
29 | 8.00000E-03 1.042E+01 9.935E+00
30 | 1.00000E-02 5.379E+00 4.987E+00
31 | 1.50000E-02 1.699E+00 1.402E+00
32 | 2.00000E-02 8.230E-01 5.663E-01
33 | 3.00000E-02 3.790E-01 1.616E-01
34 | 4.00000E-02 2.688E-01 7.216E-02
35 | 5.00000E-02 2.264E-01 4.360E-02
36 | 6.00000E-02 2.048E-01 3.264E-02
37 | 8.00000E-02 1.823E-01 2.617E-02
38 | 1.00000E-01 1.693E-01 2.545E-02
39 | 1.50000E-01 1.492E-01 2.745E-02
40 | 2.00000E-01 1.358E-01 2.942E-02
41 | 3.00000E-01 1.175E-01 3.164E-02
42 | 4.00000E-01 1.052E-01 3.249E-02
43 | 5.00000E-01 9.598E-02 3.269E-02
44 | 6.00000E-01 8.873E-02 3.254E-02
45 | 8.00000E-01 7.793E-02 3.176E-02
46 | 1.00000E+00 7.006E-02 3.074E-02
47 | 1.25000E+00 6.265E-02 2.938E-02
48 | 1.50000E+00 5.701E-02 2.807E-02
49 | 2.00000E+00 4.895E-02 2.583E-02
50 | 3.00000E+00 3.931E-02 2.259E-02
51 | 4.00000E+00 3.369E-02 2.045E-02
52 | 5.00000E+00 3.000E-02 1.895E-02
53 | 6.00000E+00 2.741E-02 1.786E-02
54 | 8.00000E+00 2.401E-02 1.639E-02
55 | 1.00000E+01 2.193E-02 1.547E-02
56 | 1.50000E+01 1.915E-02 1.422E-02
57 | 2.00000E+01 1.787E-02 1.362E-02
58 |
--------------------------------------------------------------------------------
/Switch.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Script to switch Network interfaces between DHCP and Elphel
5 | First version based on reading the values from their respective outputs
6 | New version simply sets to Elphel as default when called without a flag.
7 | Options -p or -p switch to the *P*SI *D*HCP server
8 | """
9 |
10 | import os
11 | from optparse import OptionParser
12 |
13 | # Setup the Options
14 | Parser = OptionParser()
15 | Usage = 'Usage: % prog [Options] arg'
16 |
17 | Parser.add_option('-e', '--Elphel', dest='Elphel',
18 | help='Switch the network interface to work with the '
19 | 'Elphel camera (default)s',
20 | default=True,
21 | action='store_true')
22 | Parser.add_option('-d', '--DHCP', dest='DHCP',
23 | help='Switch the network interface to *D*HCP, to '
24 | 'work with PSI ethernet',
25 | action='store_true')
26 | Parser.add_option('-p', '--PSI', dest='PSI',
27 | help='Switch the network interface to DHCP, to work '
28 | 'with *P*SI ethernet',
29 | action='store_true')
30 | (Options, Arguments) = Parser.parse_args()
31 |
32 | if Options.DHCP or Options.PSI:
33 | print ('Setting network interface to "PSI" with '
34 | '"sudo dhclient eth0 > /dev/null"')
35 | os.system('sudo dhclient eth0 > /dev/null')
36 | print
37 | print ('Unplug the Ethernet cable from the Camera, plug in the PSI '
38 | 'Ethernet cable and interweb')
39 | elif Options.Elphel:
40 | print ('Setting network interface to "Elphel" with '
41 | '"sudo ifconfig eth0 192.168.0.1 > /dev/null"')
42 | os.system('sudo ifconfig eth0 192.168.0.1 > /dev/null')
43 | print
44 | print ('Uplug the PSI Ethernet cable, plug in both ends of the Elphel '
45 | 'ethernet cable, powercycle the camera with the POE supply and '
46 | 'wait a while for the camera to boot')
47 |
48 | if Options.DHCP or Options.PSI:
49 | print ('If you just switched from Elphel to to PSI, then it is probably '
50 | 'a good idea commit all your changes and push them to the remote '
51 | 'repository. You can then pull all versioned changes from there.')
52 | print '---'
53 | print 'cd;git commit -a;git push'
54 | print '---'
55 | print 'would to that all at once...'
56 | print
57 | print 'Or you can copy all the non-versioned stuff to AFS with'
58 | print '---'
59 | print 'cd;bash SyncToAFS.cmd'
60 | print '---'
61 |
--------------------------------------------------------------------------------
/nist/blood.dat:
--------------------------------------------------------------------------------
1 | #http://physics.nist.gov/PhysRefData/XrayMassCoef/ComTab/blood.html
2 | #Blood, Whole (ICRU-44)
3 | #________________________________________
4 | #
5 | # Energy μ/ρ μen/ρ
6 | # (MeV) (cm2/g) (cm2/g)
7 | #________________________________________
8 |
9 | 1.00000E-03 3.806E+03 3.795E+03
10 | 1.03542E-03 3.473E+03 3.462E+03
11 | 1.07210E-03 3.167E+03 3.158E+03
12 | 11 K 1.07210E-03 3.173E+03 3.164E+03
13 | 1.50000E-03 1.282E+03 1.278E+03
14 | 2.00000E-03 5.737E+02 5.718E+02
15 | 2.14550E-03 4.700E+02 4.682E+02
16 | 15 K 2.14550E-03 4.722E+02 4.703E+02
17 | 2.30297E-03 3.858E+02 3.841E+02
18 | 2.47200E-03 3.149E+02 3.134E+02
19 | 16 K 2.47200E-03 3.186E+02 3.168E+02
20 | 2.64140E-03 2.633E+02 2.618E+02
21 | 2.82240E-03 2.175E+02 2.161E+02
22 | 17 K 2.82240E-03 2.219E+02 2.201E+02
23 | 3.00000E-03 1.862E+02 1.846E+02
24 | 3.60740E-03 1.088E+02 1.076E+02
25 | 19 K 3.60740E-03 1.109E+02 1.094E+02
26 | 4.00000E-03 8.187E+01 8.066E+01
27 | 5.00000E-03 4.232E+01 4.147E+01
28 | 6.00000E-03 2.458E+01 2.393E+01
29 | 7.11200E-03 1.479E+01 1.425E+01
30 | 26 K 7.11200E-03 1.514E+01 1.450E+01
31 | 8.00000E-03 1.068E+01 1.013E+01
32 | 1.00000E-02 5.519E+00 5.096E+00
33 | 1.50000E-02 1.744E+00 1.440E+00
34 | 2.00000E-02 8.428E-01 5.831E-01
35 | 3.00000E-02 3.852E-01 1.669E-01
36 | 4.00000E-02 2.715E-01 7.443E-02
37 | 5.00000E-02 2.278E-01 4.477E-02
38 | 6.00000E-02 2.057E-01 3.332E-02
39 | 8.00000E-02 1.827E-01 2.645E-02
40 | 1.00000E-01 1.695E-01 2.559E-02
41 | 1.50000E-01 1.492E-01 2.749E-02
42 | 2.00000E-01 1.358E-01 2.944E-02
43 | 3.00000E-01 1.176E-01 3.164E-02
44 | 4.00000E-01 1.052E-01 3.249E-02
45 | 5.00000E-01 9.598E-02 3.269E-02
46 | 6.00000E-01 8.874E-02 3.254E-02
47 | 8.00000E-01 7.793E-02 3.177E-02
48 | 1.00000E+00 7.007E-02 3.074E-02
49 | 1.25000E+00 6.265E-02 2.938E-02
50 | 1.50000E+00 5.701E-02 2.807E-02
51 | 2.00000E+00 4.896E-02 2.584E-02
52 | 3.00000E+00 3.932E-02 2.260E-02
53 | 4.00000E+00 3.371E-02 2.046E-02
54 | 5.00000E+00 3.002E-02 1.897E-02
55 | 6.00000E+00 2.743E-02 1.788E-02
56 | 8.00000E+00 2.405E-02 1.642E-02
57 | 1.00000E+01 2.196E-02 1.550E-02
58 | 1.50000E+01 1.920E-02 1.425E-02
59 | 2.00000E+01 1.793E-02 1.366E-02
60 |
--------------------------------------------------------------------------------
/Demonstrator/detect_lines_in_checkerboard.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Script to detect horizontal and vertical lines on a sliding window along images
5 | The script is based on the answers to
6 | [this Stack Overflow question](http://stackoverflow.com/q/7227074/323100)
7 | """
8 |
9 | from __future__ import division
10 | import matplotlib
11 | import matplotlib.pylab as plt
12 | import os
13 | import cv2
14 | import math
15 |
16 | BasePath = '/afs/psi.ch/project/EssentialMed/Images/DetectorElectronicsTests/' \
17 | 'EssentialLab/Valerie'
18 |
19 | tcpIpPool = ['192.168.1.31', '192.168.1.32', '192.168.1.33', '192.168.1.34',
20 | '192.168.1.35', '192.168.1.36', '192.168.1.37', '192.168.1.38',
21 | '192.168.1.39', '192.168.1.40', '192.168.1.41', '192.168.1.42']
22 |
23 | ImageName = tcpIpPool[0] + '.png'
24 |
25 | plt.figure(figsize=[16, 9])
26 |
27 | InputImage = cv2.imread(os.path.join(BasePath, ImageName))
28 |
29 | RegionWidth = 200
30 | for i in range(0, InputImage.shape[1] - RegionWidth, RegionWidth):
31 | print i
32 | plt.clf()
33 | plt.suptitle(' '.join(['Region of size', str(RegionWidth),
34 | 'starting at px.', str(i)]))
35 | img = InputImage[:, i:i + RegionWidth, :]
36 | # img = cv2.GaussianBlur(img, (5, 5), 0)
37 | gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
38 | edges = cv2.Canny(gray, 80, 120)
39 | lines = cv2.HoughLinesP(cv2.flip(edges, flipCode=0), 1, math.pi / 2, 2,
40 | None, 30, 1)
41 | for line in lines[0]:
42 | pt1 = (line[0], line[1])
43 | pt2 = (line[2], line[3])
44 |
45 | plt.subplot(141)
46 | plt.imshow(InputImage, interpolation='none')
47 | window = matplotlib.patches.Rectangle((i, 0), RegionWidth,
48 | InputImage.shape[0], color='blue',
49 | alpha=0.25)
50 | plt.gca().add_patch(window)
51 | plt.axvline(x=i)
52 | plt.axvline(x=i + RegionWidth)
53 | plt.title('Sliding window along image')
54 | plt.subplot(142)
55 | plt.imshow(gray, interpolation='none', cmap='gray')
56 | plt.title('Region')
57 | plt.subplot(143)
58 | plt.imshow(edges, interpolation='none', cmap='gray')
59 | plt.title('Detected Edges')
60 | plt.subplot(144)
61 | plt.imshow(cv2.flip(gray, flipCode=0), interpolation='none', cmap='gray')
62 | for coordinates in lines[0]:
63 | plt.plot([coordinates[0], coordinates[2]],
64 | [coordinates[1], coordinates[3]], color='y', linestyle='-',
65 | linewidth='5', alpha=0.5)
66 | plt.plot([coordinates[0], coordinates[2]],
67 | [coordinates[1], coordinates[3]], color='k', linestyle='-')
68 | plt.xlim([0, img.shape[1]])
69 | plt.ylim([0, img.shape[0]])
70 | plt.title('Detected horizontal\nand vertical lines')
71 | plt.pause(0.001)
72 | plt.show()
73 |
--------------------------------------------------------------------------------
/nist/gadolinium.dat:
--------------------------------------------------------------------------------
1 | #http://physics.nist.gov/PhysRefData/XrayMassCoef/ComTab/gadolinium.html
2 | #Gadolinium Oxysulfide
3 | #________________________________________
4 | #
5 | # Energy μ/ρ μen/ρ
6 | # (MeV) (cm2/g) (cm2/g)
7 | #________________________________________
8 |
9 | 1.00000E-03 2.497E+03 2.487E+03
10 | 1.08867E-03 2.103E+03 2.093E+03
11 | 1.18520E-03 1.765E+03 1.756E+03
12 | 64 M5 1.18520E-03 1.984E+03 1.973E+03
13 | 1.20109E-03 2.465E+03 2.450E+03
14 | 1.21720E-03 3.644E+03 3.621E+03
15 | 64 M4 1.21720E-03 4.311E+03 4.282E+03
16 | 1.50000E-03 4.390E+03 4.361E+03
17 | 1.54400E-03 4.092E+03 4.065E+03
18 | 64 M3 1.54400E-03 4.699E+03 4.668E+03
19 | 1.61454E-03 4.235E+03 4.207E+03
20 | 1.68830E-03 3.819E+03 3.793E+03
21 | 64 M2 1.68830E-03 4.046E+03 4.019E+03
22 | 1.78195E-03 3.585E+03 3.562E+03
23 | 1.88080E-03 3.175E+03 3.154E+03
24 | 64 M1 1.88080E-03 3.311E+03 3.289E+03
25 | 2.00000E-03 2.883E+03 2.863E+03
26 | 2.47200E-03 1.752E+03 1.739E+03
27 | 16 K 2.47200E-03 1.909E+03 1.885E+03
28 | 3.00000E-03 1.205E+03 1.189E+03
29 | 4.00000E-03 5.916E+02 5.822E+02
30 | 5.00000E-03 3.371E+02 3.302E+02
31 | 6.00000E-03 2.118E+02 2.062E+02
32 | 7.24280E-03 1.306E+02 1.261E+02
33 | 64 L3 7.24280E-03 3.312E+02 2.983E+02
34 | 7.57876E-03 2.994E+02 2.667E+02
35 | 7.93030E-03 2.625E+02 2.378E+02
36 | 64 L2 7.93030E-03 3.539E+02 3.134E+02
37 | 8.00000E-03 3.470E+02 3.074E+02
38 | 8.37560E-03 3.096E+02 2.754E+02
39 | 64 L1 8.37560E-03 3.560E+02 3.152E+02
40 | 1.00000E-02 2.285E+02 2.053E+02
41 | 1.50000E-02 7.902E+01 7.232E+01
42 | 2.00000E-02 3.689E+01 3.376E+01
43 | 3.00000E-02 1.254E+01 1.125E+01
44 | 4.00000E-02 5.854E+00 5.084E+00
45 | 5.00000E-02 3.274E+00 2.732E+00
46 | 5.02391E-02 3.234E+00 2.695E+00
47 | 64 K 5.02391E-02 1.555E+01 4.677E+00
48 | 6.00000E-02 9.815E+00 3.946E+00
49 | 8.00000E-02 4.666E+00 2.453E+00
50 | 1.00000E-01 2.613E+00 1.545E+00
51 | 1.50000E-01 9.381E-01 6.043E-01
52 | 2.00000E-01 4.812E-01 3.035E-01
53 | 3.00000E-01 2.185E-01 1.225E-01
54 | 4.00000E-01 1.423E-01 7.211E-02
55 | 5.00000E-01 1.095E-01 5.218E-02
56 | 6.00000E-01 9.153E-02 4.240E-02
57 | 8.00000E-01 7.225E-02 3.322E-02
58 | 1.00000E+00 6.163E-02 2.883E-02
59 | 1.25000E+00 5.335E-02 2.558E-02
60 | 1.50000E+00 4.832E-02 2.367E-02
61 | 2.00000E+00 4.271E-02 2.183E-02
62 | 3.00000E+00 3.829E-02 2.129E-02
63 | 4.00000E+00 3.699E-02 2.198E-02
64 | 5.00000E+00 3.685E-02 2.297E-02
65 | 6.00000E+00 3.722E-02 2.397E-02
66 | 8.00000E+00 3.864E-02 2.579E-02
67 | 1.00000E+01 4.038E-02 2.730E-02
68 | 1.50000E+01 4.478E-02 2.985E-02
69 | 2.00000E+01 4.837E-02 3.099E-02
70 |
--------------------------------------------------------------------------------
/SurfaceEntranceDoseCalculator.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Script used to calculate the surface entrance dose of a certain x-ray
5 | measurement. Gives the same results as the 'Diagnostische Referenzwerte'
6 | Excel calculator on the BAG-page, in the right side-bar of http://is.gd/E2qIPA.
7 | The calculation is based on 'Merkblatt R-06-04' from BAG.
8 | The file is an extensiuon of SurfaceEntranceDose.py, which was used to plot
9 | the SED for a talk at an SLS Symposium.
10 | """
11 |
12 | from __future__ import division # fix integer division
13 | from optparse import OptionParser
14 | import sys
15 |
16 |
17 | # Use Pythons Optionparser to define and read the options, and also
18 | # give some help to the user
19 | parser = OptionParser()
20 | usage = "usage: %prog [options] arg"
21 | parser.add_option('-v', '--kilovolt', dest='kV',
22 | type='float',
23 | help='set kV',
24 | metavar='90')
25 | parser.add_option('-a', '--milliamperesecond', dest='mAs',
26 | type='float',
27 | help='set mAs',
28 | metavar='125')
29 | parser.add_option('-d', '--distance', dest='FocusDistance',
30 | type='float',
31 | default=140.,
32 | help='Focus distance in cm. Defaults to 140 cm',
33 | metavar='120')
34 | (options, args) = parser.parse_args()
35 |
36 | # show the help if no parameters are given
37 | if options.kV is None or options.mAs is None:
38 | parser.print_help()
39 | print 'Example:'
40 | print 'The command below calculates the surface entrance dose of a',\
41 | '"Chest: PA standing", where the WHO manual gives 120 kV and 2 mAs',\
42 | 'as base values. The distance is shortened fromt the default 140 cm',\
43 | 'to 1 meter.'
44 | print
45 | print sys.argv[0], '-v 120 -a 2 -d 100'
46 | print
47 | sys.exit(1)
48 |
49 | # Parameters
50 | # The K-value is based on the machine. The BAG-calculator (see below) list 0.1
51 | K = 0.1
52 | # BSF as found by Arouna2000, cited by BAG2012. *This* BSF gives the same SED
53 | # values as the XLS-calculator from BAG (http://is.gd/oTpniQ) which I copied to
54 | # /afs/psi.ch/project/EssentialMed/PresentationsAndInfo/BAG/R-0 DRWCalc 5.0.xls
55 | BSF = 1.35
56 | # BSF as found in BAG2012. "Der ueber verschiedene Anlagen gemittelte
57 | # Korrekturfaktor betrug 1.15"
58 | # BSF = 1.15
59 |
60 | # calculating while converting Focusdistance from m to cm
61 | SED = K * (options.kV / 100) ** 2 * options.mAs *\
62 | (100 / options.FocusDistance) ** 2 * BSF
63 | print
64 | print 'Calculating the surface entrance dose for an x-ray pulse with'
65 | print ' *', options.kV, 'kV'
66 | print ' *', options.mAs, 'mAs and'
67 | print ' *', options.FocusDistance / 100, 'm focal distance.'
68 | print ' * the characteristic constant K of the setup was set to', K,\
69 | 'mGy/mA.'
70 | print ' * the back-scatter factor was set to', str(BSF) + '.'
71 | print 'The surface entrance dose is thus SED = K*(U/100)^2*Q*(1/FOD)^2*BSF=' +\
72 | str(round(SED, 3)), 'mGy'
73 |
--------------------------------------------------------------------------------
/Analysis/DisplayExperimentID.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf8 -*-
2 |
3 | """
4 | Script to display only some experiments, based on either choice of ID or choice
5 | of component
6 | """
7 |
8 | import os
9 | import matplotlib.pyplot as plt
10 | import numpy as np
11 | from functions import estimate_image_noise
12 |
13 | ID = [
14 | 8466438,
15 | 8466246,
16 | 8596690,
17 | 8595968,
18 | 5808743,
19 | 5814382,
20 | 5814190,
21 | 9078293,
22 | 9074884,
23 | 9071872,
24 | 8466170,
25 | 9080226,
26 | 7733379
27 | ]
28 |
29 | # ID = [123456]
30 |
31 | ID = [9078313]
32 |
33 | getfromHD = False
34 | if getfromHD:
35 | RootFolder = '/media/WINDOWS'
36 | else:
37 | RootFolder = '/afs/psi.ch/project/EssentialMed/MasterArbeitBFH/XrayImages'
38 |
39 | # Get all folders (Experiment) and ExperimentIDs inside StartingFolder
40 | print 'Looking for the folder with the given Experiment IDs '
41 | Folder = []
42 | ExperimentID = []
43 | for root, dirs, files in os.walk(RootFolder):
44 | # Go through all the directories, see if the last foldername is a number,
45 | # and it's in the list of IDs above,
46 | # Go through all the directories
47 | try:
48 | # If the last foldername is a number,
49 | int(os.path.basename(root))
50 | # see if it's in the list of IDs above,
51 | if int(os.path.basename(root)) in ID:
52 | print 'Experiment', os.path.basename(root), 'found in', \
53 | os.path.dirname(root)
54 | Folder.append(root)
55 | ExperimentID.append(int(os.path.basename(root)))
56 | # print len(Folder), len(ExperimentID), len(ID)
57 | # otherwise just pass
58 | except ValueError:
59 | continue
60 |
61 | if getfromHD:
62 | print 'The experiment', ID, 'is in the folder', Folder, 'on', RootFolder
63 | exit()
64 |
65 | # Warn the user if we were not able to find an image...
66 | if len(ExperimentID) != len(ID):
67 | for i in ID:
68 | if i not in ExperimentID:
69 | print '\nExperiment ID', i, 'was not found in', RootFolder
70 | exit('Hope it is only a typo...')
71 |
72 | # Extract data from the images
73 | print 'Reading and preparing images'
74 | Images = [plt.imread(i + '.image.corrected.stretched.png') for i in Folder]
75 | Mean = [np.mean(i) * 255 for i in Images]
76 | STD = [np.std(i) * 255 for i in Images]
77 | Noise = [estimate_image_noise(i) * 255 for i in Images]
78 |
79 | print 'Mean goes from', round(min(Mean), 1), 'to', round(max(Mean), 1)
80 | print 'STD goes from', round(min(STD), 1), 'to', round(max(STD), 1)
81 | print 'Noise goes from', round(min(Noise), 1), 'to', round(max(Noise), 1)
82 |
83 | plt.figure(figsize=(20, 4))
84 | for c, i in enumerate(Images):
85 | print 'Reading and preparing image', c, 'of', len(Folder)
86 | plt.subplot(1, len(Folder), c + 1)
87 | plt.imshow(i, cmap='bone')
88 | title = '\n'.join(['ID ' + str(ExperimentID[c]), 'Mean ' + str(round(
89 | Mean[c], 1)), 'STD ' + str(round(STD[c], 1)), 'Noise ' +
90 | str(round(Noise[c]))])
91 | plt.title(title)
92 | plt.axis('off')
93 | plt.tight_layout()
94 | plt.show()
95 |
--------------------------------------------------------------------------------
/nist/cesium.dat:
--------------------------------------------------------------------------------
1 | #http://physics.nist.gov/PhysRefData/XrayMassCoef/ComTab/cesium.html
2 | #Cesium Iodide
3 | #________________________________________
4 | #
5 | # Energy μ/ρ μen/ρ
6 | # (MeV) (cm2/g) (cm2/g)
7 | #________________________________________
8 |
9 | 1.00000E-03 9.234E+03 9.213E+03
10 | 1.03199E-03 8.653E+03 8.633E+03
11 | 1.06500E-03 8.098E+03 8.080E+03
12 | 55 M2 1.06500E-03 8.339E+03 8.320E+03
13 | 1.06854E-03 8.281E+03 8.262E+03
14 | 1.07210E-03 8.224E+03 8.205E+03
15 | 53 M1 1.07210E-03 8.387E+03 8.368E+03
16 | 1.14230E-03 7.344E+03 7.327E+03
17 | 1.21710E-03 6.413E+03 6.398E+03
18 | 55 M1 1.21710E-03 6.569E+03 6.553E+03
19 | 1.50000E-03 4.132E+03 4.120E+03
20 | 2.00000E-03 2.114E+03 2.104E+03
21 | 3.00000E-03 7.880E+02 7.809E+02
22 | 4.00000E-03 3.836E+02 3.776E+02
23 | 4.55710E-03 2.752E+02 2.696E+02
24 | 53 L3 4.55710E-03 5.174E+02 4.936E+02
25 | 4.70229E-03 4.851E+02 4.628E+02
26 | 4.85210E-03 4.510E+02 4.303E+02
27 | 53 L2 4.85210E-03 5.637E+02 5.332E+02
28 | 5.00000E-03 5.296E+02 5.012E+02
29 | 5.01190E-03 5.268E+02 4.985E+02
30 | 55 L3 5.01190E-03 7.511E+02 7.037E+02
31 | 5.09924E-03 7.193E+02 6.744E+02
32 | 5.18810E-03 6.881E+02 6.457E+02
33 | 53 L1 5.18810E-03 7.453E+02 6.987E+02
34 | 5.27305E-03 7.196E+02 6.716E+02
35 | 5.35940E-03 6.875E+02 6.454E+02
36 | 55 L2 5.35940E-03 7.923E+02 7.397E+02
37 | 5.53401E-03 7.323E+02 6.847E+02
38 | 5.71430E-03 6.761E+02 6.331E+02
39 | 55 L1 5.71430E-03 7.268E+02 6.795E+02
40 | 6.00000E-03 6.448E+02 6.043E+02
41 | 8.00000E-03 3.071E+02 2.906E+02
42 | 1.00000E-02 1.711E+02 1.624E+02
43 | 1.50000E-02 5.815E+01 5.486E+01
44 | 2.00000E-02 2.686E+01 2.496E+01
45 | 3.00000E-02 9.045E+00 8.071E+00
46 | 3.31694E-02 6.923E+00 6.088E+00
47 | 53 K 3.31694E-02 2.122E+01 9.086E+00
48 | 3.45483E-02 2.687E+01 8.529E+00
49 | 3.59846E-02 1.719E+01 7.990E+00
50 | 55 K 3.59846E-02 3.027E+01 1.059E+01
51 | 4.00000E-02 2.297E+01 9.395E+00
52 | 5.00000E-02 1.287E+01 6.596E+00
53 | 6.00000E-02 7.921E+00 4.586E+00
54 | 8.00000E-02 3.677E+00 2.399E+00
55 | 1.00000E-01 2.035E+00 1.391E+00
56 | 1.50000E-01 7.290E-01 4.951E-01
57 | 2.00000E-01 3.805E-01 2.401E-01
58 | 3.00000E-01 1.818E-01 9.634E-02
59 | 4.00000E-01 1.237E-01 5.828E-02
60 | 5.00000E-01 9.809E-02 4.366E-02
61 | 6.00000E-01 8.373E-02 3.657E-02
62 | 8.00000E-01 6.769E-02 2.987E-02
63 | 1.00000E+00 5.848E-02 2.657E-02
64 | 1.25000E+00 5.110E-02 2.402E-02
65 | 1.50000E+00 4.644E-02 2.243E-02
66 | 2.00000E+00 4.123E-02 2.089E-02
67 | 3.00000E+00 3.721E-02 2.059E-02
68 | 4.00000E+00 3.616E-02 2.144E-02
69 | 5.00000E+00 3.622E-02 2.255E-02
70 | 6.00000E+00 3.673E-02 2.364E-02
71 | 8.00000E+00 3.838E-02 2.563E-02
72 | 1.00000E+01 4.030E-02 2.725E-02
73 | 1.50000E+01 4.492E-02 2.992E-02
74 | 2.00000E+01 4.867E-02 3.114E-02
75 |
--------------------------------------------------------------------------------
/Analysis/Comparator.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf8 -*-
2 |
3 | """
4 | Script to compare the contents of Ivans HD with the stuff present on AFS.
5 | Due to space shortage, some folders might have been missed during copying.
6 |
7 | And since a large part of the folders were already processed, we cannot just
8 | use `rsync` or that will copy over all the files that already were deleted.
9 | So we use a little routine to find missing folders and rsync only those one
10 | after the other.
11 | This should also take care of the speed issue should the script to have to run
12 | several times.
13 | """
14 |
15 | import os
16 | import subprocess
17 | import errno
18 |
19 |
20 | def readit(InputFolder):
21 | '''
22 | Get all folders (Experiment) and ExperimentIDs inside StartingFolder
23 | '''
24 | print 'Looking for Experiment ID folders in', InputFolder
25 | print 'This will take a while'
26 | Folder = []
27 | ExperimentID = []
28 | for root, dirs, files in os.walk(InputFolder):
29 | # Go through all the directories and see if the last foldername is a
30 | # number
31 | try:
32 | if int(os.path.basename(root)):
33 | Folder.append(root)
34 | ExperimentID.append(int(os.path.basename(root)))
35 | # otherwise just continue
36 | except ValueError:
37 | continue
38 |
39 | return Folder, ExperimentID
40 |
41 | # Read *all* experiment IDs from HD
42 | RootFolderHD = ('/media/WINDOWS/Aptina')
43 | FolderHD, IDHD = readit(RootFolderHD)
44 |
45 | # Read *all* experiment IDs from AFS
46 | RootFolderAFS = ('/afs/psi.ch/project/EssentialMed/MasterArbeitBFH/XrayImages')
47 | FolderAFS, IDAFS = readit(RootFolderAFS)
48 |
49 | for counter, experiment in enumerate(IDHD):
50 | print 5 * '-', '|', str(counter + 1) + '/' + str(len(IDHD)), '|', 60 * '-'
51 | if experiment not in IDAFS:
52 | InputPath = FolderHD[IDHD.index(experiment)]
53 | OutputPath = os.path.dirname(os.path.join(RootFolderAFS,
54 | FolderHD[IDHD.index(experiment)][len(RootFolderHD) + 1:]))
55 | print 'Experiment', experiment, 'was not found on AFS'
56 | print
57 | print 'It should be moved from'
58 | print InputPath
59 | print 'to'
60 | print OutputPath
61 | # rsync can only mkdir ONE level, so we first make the path to copy to
62 | try:
63 | os.makedirs(OutputPath)
64 | except OSError as e:
65 | if e.errno != errno.EEXIST:
66 | # If the error is not about the directory existing, raise it
67 | # again
68 | raise
69 | rsynccommand = ['rsync', '-ar', InputPath, OutputPath]
70 | print
71 | print 'rsyncing it now with the command'
72 | print ' '.join(rsynccommand)
73 | synchronizeit = subprocess.Popen(rsynccommand, stdout=subprocess.PIPE)
74 | output, error = synchronizeit.communicate()
75 | if error:
76 | print 'The below error happened'
77 | print error
78 | break
79 | else:
80 | print 'Experiment', experiment, 'from'
81 | print FolderHD[IDHD.index(experiment)]
82 | print 'is already on AFS at'
83 | print FolderAFS[IDAFS.index(experiment)]
84 | print 'Done with experiment', experiment
85 |
86 | print 80 * '-'
87 | print 'Done with all', len(IDHD), 'experiment IDs on', RootFolderHD
88 |
--------------------------------------------------------------------------------
/GOTTHARD/Photon-Calculation.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Plot Attenuation and transmission of GOTTHARD detector element
5 | """
6 |
7 | from __future__ import division
8 | import matplotlib.pylab as plt
9 | import os
10 | import glob
11 | import numpy as np
12 |
13 | GOTTHARDArea = 1130 * (50 / 1000) * 2 # mm
14 | Distance = 163 # cm
15 | ScintillatorArea = 430 * 430 # mm
16 | print 'The area of the GOTTHARD sensor we used was', int(GOTTHARDArea), 'mm²'
17 | print 'This is', int(round(ScintillatorArea / GOTTHARDArea)), 'times smaller',\
18 | 'than the scintillator we plan to use (430 x 430 mm²)'
19 |
20 | SiliconAttenuation = np.loadtxt('Si_Attenuation.dat')
21 | SiliconTransmission = np.loadtxt('Si_Transmission.dat')
22 | SiliconDensity = 2.329 # g/cm³
23 | SiliconThickness = 320 # um
24 |
25 | plt.figure()
26 | plt.hold(True)
27 | plt.subplot(1, 2, 1)
28 | plt.plot(SiliconAttenuation[:, 0] * 1000,
29 | 1 - (np.exp(1) ** - (SiliconAttenuation[:, 1] * SiliconDensity *
30 | SiliconThickness / 10000)), color='k')
31 | plt.xlabel('Photon Energy [keV]')
32 | plt.rc('text', usetex=True)
33 | plt.rc('font', family='serif')
34 | plt.ylabel(r'Attenuation coefficient $\frac{\mu}{\rho}$ [cm2/g]')
35 | plt.title('Attenuation')
36 | plt.xlim([0, 120])
37 | plt.ylim([0, 1])
38 |
39 | from scipy import interpolate
40 | x = SiliconAttenuation[:, 0] * 1000
41 | y = (np.exp(- (SiliconAttenuation[:, 1] * SiliconDensity * SiliconThickness /
42 | 10000)))
43 | f1 = interpolate.interp1d(x, y)
44 | f2 = interpolate.interp1d(x, y, kind='cubic')
45 |
46 | xnew = np.arange(1, 120, 0.1)
47 |
48 | plt.subplot(1, 2, 2)
49 | plt.plot(SiliconTransmission[:, 0] / 1000, SiliconTransmission[:, 1],
50 | color='k', label='from Anna')
51 | plt.plot(SiliconAttenuation[:, 0] * 1000,
52 | (np.exp(- (SiliconAttenuation[:, 1] * SiliconDensity *
53 | SiliconThickness / 10000))), 'gD',
54 | label='from NIST (1-Attenuation)')
55 | plt.plot(xnew, f1(xnew) + 0.1, 'r', label='Int')
56 | plt.plot(xnew, f2(xnew) + 0.2, 'b', label='Int')
57 | # plt.legend(loc='best')
58 | plt.xlabel('Photon Energy [keV]')
59 | plt.ylabel('Tranmission')
60 | plt.title('Transmission for a thickness of 320 um')
61 | plt.xlim([0, 120])
62 | # plt.ylim([0, 1])
63 |
64 | # plt.savefig('Si_Attenuation_Transmission.pdf')
65 | plt.show()
66 |
67 | Spectrapath = '/afs/psi.ch/project/EssentialMed/Images/' \
68 | 'GOTTHARD_and_TIS/GOTTHARD'
69 | Spectra = sorted(glob.glob(os.path.join(Spectrapath, '*.txt')))
70 |
71 | FileName = [os.path.basename(item) for item in Spectra]
72 | Data = [np.loadtxt(item) for item in Spectra]
73 | DataName = [open(item).readlines()[0].split()[0][1:-2] for item in Spectra]
74 |
75 | # Get Filenames of Spectra and split it up into the desired values like kV, mAs
76 | # and exposure time with some basic string handling.
77 | Modality = [item.split('_')[0] for item in FileName]
78 | Energy = [int(item.split('_')[1][:-2]) for item in FileName]
79 | Current = [int(item.split('_')[2][:-2]) for item in FileName]
80 | mAs = [float(item.split('_')[3][:-3]) for item in FileName]
81 | ExposureTime = [int(item.split('_')[4][:-6]) for item in FileName]
82 |
83 | Frames = [open(item).readlines()[0].split()[1] for item in Spectra]
84 | BinCenter = [open(item).readlines()[1].split()[0] for item in Spectra]
85 | Photons = [open(item).readlines()[1].split()[1] for item in Spectra]
86 | PhotonsPerFrame = [open(item).readlines()[1].split()[2] for item in Spectra]
87 |
--------------------------------------------------------------------------------
/DetectorMockup/ScintillatorBackplates.scad:
--------------------------------------------------------------------------------
1 | // Scintillator backplate
2 |
3 | // Basic length variables
4 | CenterSzintillator = [459/2, 380 - 80 - 172/2, 0];
5 | screwheight = 20;
6 | screwradius = 3;
7 |
8 | // Toshiba Szintillator
9 | SizeToshiba = [236, 172, 20];
10 | color ("black", 1) {
11 | translate(CenterSzintillator+SizeToshiba/2+[screwradius+2,-20,-SizeToshiba[2]]){
12 | cylinder(h = screwheight , r=3);
13 | }
14 | translate(CenterSzintillator+SizeToshiba/2+[screwradius+2,-SizeToshiba[1]+20,-13]){
15 | cylinder(h = screwheight , r=3);
16 | }
17 | translate(CenterSzintillator-SizeToshiba/2-[screwradius+2,-20,0]){
18 | cylinder(h = screwheight , r=3);
19 | }
20 | translate(CenterSzintillator-SizeToshiba/2-[screwradius+2,-SizeToshiba[1]+20,0]){
21 | cylinder(h = screwheight , r=3);
22 | }
23 | translate(CenterSzintillator-SizeToshiba/2-[-20,-SizeToshiba[1]-screwradius,0]){
24 | cylinder(h = screwheight , r=3);
25 | }
26 | translate(CenterSzintillator-SizeToshiba/2-[-SizeToshiba[0]+20,-SizeToshiba[1]-screwradius,0]){
27 | cylinder(h = screwheight , r=3);
28 | }
29 | }
30 | difference(){
31 | cube([459, 380, 6]);
32 | translate(CenterSzintillator-SizeToshiba/2){
33 | cube(SizeToshiba);
34 | }
35 | }
36 |
37 | // Pingseng Szintillator
38 | SizePinseng = [200, 200, 20];
39 | translate([500,0,0]){
40 | color ("black", 1) {
41 | translate(CenterSzintillator+SizePinseng/2+[screwradius+2,-20,-20]){
42 | cylinder(h = screwheight , r=3);
43 | }
44 | translate(CenterSzintillator+SizePinseng/2+[screwradius+2,-SizePinseng[1]+20,-SizePinseng[2]]){
45 | cylinder(h = screwheight , r=3);
46 | }
47 | translate(CenterSzintillator-SizePinseng/2-[screwradius+2,-20,0]){
48 | cylinder(h = screwheight , r=3);
49 | }
50 | translate(CenterSzintillator-SizePinseng/2-[screwradius+2,-SizePinseng[1]+20,0]){
51 | cylinder(h = screwheight , r=3);
52 | }
53 | translate(CenterSzintillator-SizePinseng/2-[-20,-SizePinseng[1]-screwradius,0]){
54 | cylinder(h = screwheight , r=3);
55 | }
56 | translate(CenterSzintillator-SizePinseng/2-[-SizePinseng[0]+20,-SizePinseng[1]-screwradius,0]){
57 | cylinder(h = screwheight , r=3);
58 | }
59 | }
60 | difference(){
61 | cube([459, 380, 6]);
62 | translate(CenterSzintillator-SizePinseng/2){
63 | cube(SizePinseng);
64 | }
65 | }
66 | }
67 |
68 | // Applied Scintillation Technologies Szintillator
69 | SizeAppScinTech = [106, 106, 20];
70 | translate([1000,0,0]){
71 | color ("black", 1) {
72 | translate(CenterSzintillator+SizeAppScinTech/2+[screwradius+2,-20,-20]){
73 | cylinder(h = screwheight , r=3);
74 | }
75 | translate(CenterSzintillator+SizeAppScinTech/2+[screwradius+2,-SizeAppScinTech[1]+20,-SizeAppScinTech[2]]){
76 | cylinder(h = screwheight , r=3);
77 | }
78 | translate(CenterSzintillator-SizeAppScinTech/2-[screwradius+2,-20,0]){
79 | cylinder(h = screwheight , r=3);
80 | }
81 | translate(CenterSzintillator-SizeAppScinTech/2-[screwradius+2,-SizeAppScinTech[1]+20,0]){
82 | cylinder(h = screwheight , r=3);
83 | }
84 | translate(CenterSzintillator-SizeAppScinTech/2-[-20,-SizeAppScinTech[1]-screwradius,0]){
85 | cylinder(h = screwheight , r=3);
86 | }
87 | translate(CenterSzintillator-SizeAppScinTech/2-[-SizeAppScinTech[0]+20,-SizeAppScinTech[1]-screwradius, 0]){
88 | cylinder(h = screwheight , r=3);
89 | }
90 | }
91 | difference(){
92 | cube([459, 380, 6]);
93 | translate(CenterSzintillator-SizeAppScinTech/2){
94 | cube(SizeAppScinTech);
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Demonstrator/darkimages.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Script to analyze the "dark" images from the experiments in the EssentialLab
5 | """
6 |
7 | import glob
8 | import os
9 | import numpy
10 | import matplotlib.pylab as plt
11 | import matplotlib.patches as patches
12 |
13 |
14 | def processimage(inputimage, clip=3):
15 | """
16 | Clip image brightness to "mean +- 3 STD" (by default). Another value can
17 | be given. This is applied to the input images if the -c commandline
18 | parameter is given.
19 | """
20 | return numpy.clip(inputimage,
21 | numpy.mean(inputimage) - (clip * numpy.std(inputimage)),
22 | numpy.mean(inputimage) + (clip * numpy.std(inputimage)))
23 |
24 |
25 | def my_display_histogram(img, howmanybins=128, histogramcolor='k',
26 | rangecolor='r', clip=3):
27 | """
28 | Display the histogram of an input image, including the ranges we clip
29 | the gray values to
30 | """
31 | plt.hist(img.flatten(), bins=howmanybins, histtype='stepfilled',
32 | fc=histogramcolor, alpha=0.309)
33 | plt.axvline(x=numpy.mean(img) - clip * numpy.std(img), color=rangecolor,
34 | linestyle='--')
35 | plt.axvline(x=numpy.mean(img), color='k', linestyle='--')
36 | plt.axvline(x=numpy.mean(img) + clip * numpy.std(img),
37 | color=rangecolor, linestyle='--')
38 | # turn off y-ticks: http://stackoverflow.com/a/2176591/323100
39 | plt.gca().axes.get_yaxis().set_ticks([])
40 | plt.title('Histogram. Black = mean\nRed = Display range')
41 |
42 | BasePath = '/afs/psi.ch/user/h/haberthuer/EssentialMed/Images/' \
43 | 'DetectorElectronicsTests/EssentialLab/'
44 |
45 | FileNames = sorted([item for item in glob.glob(os.path.join(BasePath, '*',
46 | '*36.gray'))])
47 |
48 | # Select only dark images
49 | DarkFolderList = ['1421327978_noxray',
50 | '1421327947_noxray',
51 | '1421156423_output',
52 | '1421155907_output']
53 | DarkFileNames = []
54 | for i in FileNames:
55 | if os.path.split(os.path.dirname(i))[1] in DarkFolderList:
56 | DarkFileNames.append(i)
57 |
58 | print 'Reading all %s images' % len(FileNames)
59 | Images = [numpy.fromfile(item, dtype=numpy.uint16, count=-1,
60 | sep='').reshape(1024, 1280) for item in FileNames]
61 | print 'Reading only %s dark images' % len(DarkFileNames)
62 | DarkImages = [numpy.fromfile(item, dtype=numpy.uint16, count=-1,
63 | sep='').reshape(1024, 1280) for item in
64 | DarkFileNames]
65 |
66 | plt.figure('dark images', figsize=[16, 9])
67 | for counter, image in enumerate(DarkImages):
68 | plt.subplot(3, len(DarkImages), counter + 1)
69 | plt.imshow(processimage(image), interpolation='bicubic', cmap='gray_r')
70 | Zoom = patches.Rectangle((333, 456), width=100, height=50, color='g',
71 | alpha=0.618)
72 | plt.gcf().gca().add_patch(Zoom)
73 |
74 | FigureTitle = os.path.basename(os.path.split(FileNames[counter])[0]), \
75 | '\n', os.path.basename(FileNames[counter]), '\nmean',\
76 | str(round(numpy.mean(processimage(image)))), '\nSTD', \
77 | str(round(numpy.std(processimage(image))))
78 | plt.title(' '.join(FigureTitle))
79 | plt.axis('off')
80 | plt.subplot(3, len(DarkImages), counter + len(DarkImages) + 1)
81 | plt.imshow(processimage(image)[333:333 + 50, 456:456 + 100],
82 | interpolation='nearest', cmap='gray_r')
83 | plt.title('Zoomed region')
84 | plt.axis('off')
85 | plt.subplot(3, len(DarkImages), counter + 2 * len(DarkImages) + 1)
86 | my_display_histogram(image)
87 | plt.xlim([0, 256])
88 |
89 | plt.show()
90 |
--------------------------------------------------------------------------------
/aptina/data/board_data/DualMipiFPGA.cdat:
--------------------------------------------------------------------------------
1 | // Auto Generated by RegisterDefinitions.pl script.
2 | // REGDEF = {ADDR, TYPE, MASK, RW, DEFAULT, DESC, DETAIL}
3 | // {BITDEF, MASK, RW, DESC, DETAIL}
4 | // {BITDEF, MASK, RW, DESC, DETAIL}
5 | // ...
6 | // {BITDEF, MASK, RW, DESC, DETAIL}
7 |
8 | [CHIP_DESCRIPTOR]
9 | CHIPNAME = "DualMipiFPGA"
10 | SERIAL_BASE_ADDRESS = 0xCA
11 | SERIAL_DATA_SIZE = 16
12 | [END]
13 |
14 | [Registers]
15 | ControlReg1 = {0x00, FPGA, 0xFFF3, RW, 0x0000, "Control Reg 1", ""}
16 | {MODE_SEL, 0x0003, RW, "1-0: Board operating mode", "0: DIPSwitch controlled, 1: Sensor Parallel Mode, 2: MIPI, 3: CCP"}
17 | {MAX_MIPI_LANES, 0x000C, RO, "3-2: Number of instantiated MIPI lanes", "0: 1 lane, 1: 2 lanes, 2: illegal, 3: 4 lanes"}
18 | {LANES, 0x0030, RW, "5-4: Number of active MIPI lanes", "0: 1 lane, 1: 2 lanes, 2: illegal, 3: 4 lanes"}
19 | {VIRTUAL_CHANNEL, 0x00C0, RW, "7:6: Virtual MIPI channel", ""}
20 | {DATA_SIZE, 0x0700, RW, "10-8: CCP data width", "0: 8 bit, 1: 10 bit, 2: 12 bit 7-3: reserved"}
21 | {EMBEDDED_DATA_EN, 0x0800, RW, "11: Disable Mipi embedded data", "1: disable"}
22 | {CCP_CLASS, 0x1000, RW, "12: CCP class select", "0: Class 0, 1:Class 1/2"}
23 | {OUTPUT_EN, 0x8000, RW, "15: Disable FPGA outputs", "1: disable"}
24 | ControlReg2 = {0x01, FPGA, 0xFFFF, RO, 0x0000, "Control Reg 2", ""}
25 | {DIPSW, 0x00FF, RO, "DIPSwitch", ""}
26 | {ACTIVE_MODE, 0x0300, RO, "Current board operating mode", "0: Disabled, 1: Sensor Parallel Mode, 2: MIPI, 3: CCP"}
27 | {ACTIVE_DATA_SIZE, 0x1C00, RO, "Current CCP data width", "0: 8 bit, 1: 10 bit, 2: 12 bit 7-3: reserved"}
28 | {ACTIVE_CCP_CLASS, 0x2000, RO, "Current CCP class", "0: Class 0, 1:Class 1/2"}
29 | {ACTIVE_MIPI_LANES, 0xC000, RO, "Current active Mipi lanes", "0: 1 lane, 1: 2 lanes, 2: illegal, 3: 4 lanes"}
30 | Checksum = {0x02, FPGA, 0xFFFF, RO, 0x0000, "Video Checksum readback", "Video checksum for last ccp or mipi frame"}
31 | ErrorStatus = {0x03, FPGA, 0xFFFF, RW, 0x0000, "Error Status", ""}
32 | {CRC_ERROR, 0x0001, RO, "0: CRC Error", "1: error"}
33 | {CRC_ERROR_CLR, 0x0002, RW, "1: Clear CRC error", "1: clear CRC_ERROR bit"}
34 | {LINE_FIFO_OVFL, 0x0010, RO, "4: Line FIFO Overflow", "1: overflow"}
35 | {LINE_FIFO_OVFL_CLR, 0x0020, RW, "5: Clear Line FIFO Overflow", "1: clear LINE_FIFO_OVFL bit"}
36 | CHIP_VERSION_REG = {0x08, FPGA, 0xFFFF, RO, 0x0010, "Chip Version", "Chip Version "}
37 | VERSION = {0x12, FPGA, 0xFFFF, RO, 0x0010, "FPGA Version", "FPGA Version "}
38 | {VER_MINOR, 0x000F, RO, "3-0: Firmware Minor Version #", ""}
39 | {VER_MAJOR, 0x00F0, RO, "7-4: Firmware Major Version #", ""}
40 | {SUPER_MAJOR, 0xFF00, RO, "15-8: Firmware Super Major Version ", ""}
41 | DATESTAMPREG = {0x14, FPGA, 0xFFFF, RO, 0x0000, "Firmware Build DateStamp", "m/d/y"}
42 | TIMESTAMPREG = {0x15, FPGA, 0xFFFF, RO, 0x0000, "Firmware Build TimeStamp", "h/m/s"}
43 | [END]
--------------------------------------------------------------------------------
/Demonstrator/lineprofiler.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Line profile function used in several scripts.
5 | """
6 |
7 |
8 | def lineprofile(inputimage, coordinates=False, showimage=False, debug=False):
9 | """
10 | Function to draw a line profile from a selection in the image.
11 | Comes in handy to look at the resolution phantom in x-ray images shot with
12 | the iAi electronics prototype in the x-ray lab.
13 | Based on http://stackoverflow.com/a/7880726/323100
14 | If the user supplies coordinates (in the form of coordinates =
15 | ((x0,y0), (x1,y1)), we use those, otherwise we let the user select some.
16 | If "debug" is set to true, we show Lena.
17 |
18 | The function returns the coordinates and the lineprofile of the input image
19 | along these coordinates.
20 | """
21 |
22 | # suppress pep8 warning about potentially unreferenced variables
23 | global x1, x0, y1, y0
24 |
25 | import numpy
26 | import scipy.ndimage
27 | import matplotlib.pyplot as plt
28 | import random
29 |
30 | # Debug
31 | if debug:
32 | inputimage = scipy.misc.lena()
33 |
34 | if coordinates is False:
35 | showimage = True
36 |
37 | # Prepare image
38 | if showimage:
39 | # make large figure numbers, so we don't get into troubles with
40 | # plotting to other figure numbers
41 | plt.figure(random.randint(500, 1000), figsize=(16, 16))
42 | if debug:
43 | plt.ion()
44 | plt.imshow(inputimage, cmap='bone', vmin=numpy.min(inputimage),
45 | vmax=numpy.mean(inputimage) + 3 * numpy.std(inputimage))
46 | plt.title('Please select the end-points for the line-profile')
47 |
48 | if coordinates:
49 | # The user gave coordinates to use. Use them.
50 | x0 = coordinates[0][0]
51 | y0 = coordinates[0][1]
52 | x1 = coordinates[1][0]
53 | y1 = coordinates[1][1]
54 | else:
55 | # Let the user select a line to plot the profile from
56 | pts = []
57 | while len(pts) < 2:
58 | pts = numpy.asarray(plt.ginput(2, timeout=-1))
59 | x0 = pts[1, 0]
60 | x1 = pts[0, 0]
61 | y0 = pts[1, 1]
62 | y1 = pts[0, 1]
63 |
64 | # Interpolate a line between x0, y0 and x1, y1 with double the length of
65 | # the line
66 | length = 2 * int(numpy.hypot(x1 - x0, y1 - y0))
67 | x, y = numpy.linspace(x0, x1, length), numpy.linspace(y0, y1, length)
68 |
69 | # Extract the values along the line, using cubic interpolation
70 | profileinterpolated = scipy.ndimage.map_coordinates(numpy.transpose(
71 | inputimage), numpy.vstack((x, y)))
72 | # Just do nearest-neighbour value extraction
73 | profilenn = numpy.transpose(inputimage)[x.astype(numpy.int),
74 | y.astype(numpy.int)]
75 |
76 | if showimage:
77 | # Draw the image and line profile again
78 | plt.subplot(211)
79 | plt.imshow(inputimage, cmap='bone', vmin=numpy.min(inputimage),
80 | vmax=numpy.mean(inputimage) + 3 * numpy.std(inputimage))
81 | plt.plot((x0, x1), (y0, y1), 'r')
82 | plt.plot(x0, y0, 'yo')
83 | plt.plot(x1, y1, 'ko')
84 | plt.axis('image')
85 | plt.draw()
86 |
87 | plt.subplot(212)
88 | plt.plot(profileinterpolated, 'red', label='interpolated')
89 | plt.plot(profilenn, 'orange', label='nearest neighbour')
90 | plt.plot(0, profilenn[0], 'yo', markersize=25, alpha=0.309)
91 | plt.plot(len(profilenn) - 1, profilenn[-1], 'ko', markersize=25,
92 | alpha=0.309)
93 | plt.xlim([0, len(profilenn) - 1])
94 | plt.legend(loc='best')
95 | plt.draw()
96 | plt.ioff()
97 | if debug:
98 | plt.show()
99 | return ((x0, y0), (x1, y1)), profileinterpolated
100 |
--------------------------------------------------------------------------------
/.screenrc:
--------------------------------------------------------------------------------
1 | #
2 | # Example of a user's .screenrc file
3 | #
4 |
5 | # below is from http://is.gd/YxpfN6
6 | caption always "%{= kw}%?%-Lw%?%{Kw}%n*%f %t%?(%u)%?%{kw}%?%+Lw%? %=%{= dw} %H "
7 | #
8 |
9 | hardstatus alwayslastline
10 |
11 | # This is how one can set a reattach password:
12 | # password ODSJQf.4IJN7E # "1234"
13 |
14 | # no annoying audible bell, please
15 | vbell on
16 |
17 | # detach on hangup
18 | autodetach on
19 |
20 | # don't display the copyright page
21 | startup_message off
22 |
23 | # emulate .logout message
24 | pow_detach_msg "Screen session of \$LOGNAME \$:cr:\$:nl:ended."
25 |
26 | # advertise hardstatus support to $TERMCAP
27 | # termcapinfo * '' 'hs:ts=\E_:fs=\E\\:ds=\E_\E\\'
28 |
29 | # make the shell in every window a login shell
30 | #shell -$SHELL
31 |
32 | # autoaka testing
33 | # shellaka '> |tcsh'
34 | # shellaka '$ |sh'
35 |
36 | # set every new windows hardstatus line to somenthing descriptive
37 | # defhstatus "screen: ^En (^Et)"
38 |
39 | defscrollback 1000
40 |
41 | # don't kill window after the process died
42 | # zombie "^["
43 |
44 | ################
45 | #
46 | # xterm tweaks
47 | #
48 |
49 | #xterm understands both im/ic and doesn't have a status line.
50 | #Note: Do not specify im and ic in the real termcap/info file as
51 | #some programs (e.g. vi) will not work anymore.
52 | termcap xterm hs@:cs=\E[%i%d;%dr:im=\E[4h:ei=\E[4l
53 | terminfo xterm hs@:cs=\E[%i%p1%d;%p2%dr:im=\E[4h:ei=\E[4l
54 |
55 | #80/132 column switching must be enabled for ^AW to work
56 | #change init sequence to not switch width
57 | termcapinfo xterm Z0=\E[?3h:Z1=\E[?3l:is=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;4;6l
58 |
59 | # Make the output buffer large for (fast) xterms.
60 | termcapinfo xterm* OL=10000
61 |
62 | # tell screen that xterm can switch to dark background and has function
63 | # keys.
64 | termcapinfo xterm 'VR=\E[?5h:VN=\E[?5l'
65 | termcapinfo xterm 'k1=\E[11~:k2=\E[12~:k3=\E[13~:k4=\E[14~'
66 | termcapinfo xterm 'kh=\E[1~:kI=\E[2~:kD=\E[3~:kH=\E[4~:kP=\E[H:kN=\E[6~'
67 |
68 | # special xterm hardstatus: use the window title.
69 | termcapinfo xterm 'hs:ts=\E]2;:fs=\007:ds=\E]2;screen\007'
70 |
71 | #terminfo xterm 'vb=\E[?5h$<200/>\E[?5l'
72 | termcapinfo xterm 'vi=\E[?25l:ve=\E[34h\E[?25h:vs=\E[34l'
73 |
74 | # emulate part of the 'K' charset
75 | termcapinfo xterm 'XC=K%,%\E(B,[\304,\\\\\326,]\334,{\344,|\366,}\374,~\337'
76 |
77 | # xterm-52 tweaks:
78 | # - uses background color for delete operations
79 | termcapinfo xterm ut
80 |
81 | ################
82 | #
83 | # wyse terminals
84 | #
85 |
86 | #wyse-75-42 must have flow control (xo = "terminal uses xon/xoff")
87 | #essential to have it here, as this is a slow terminal.
88 | termcapinfo wy75-42 xo:hs@
89 |
90 | # New termcap sequences for cursor application mode.
91 | termcapinfo wy* CS=\E[?1h:CE=\E[?1l:vi=\E[?25l:ve=\E[?25h:VR=\E[?5h:VN=\E[?5l:cb=\E[1K:CD=\E[1J
92 |
93 | ################
94 | #
95 | # other terminals
96 | #
97 |
98 | #make hp700 termcap/info better
99 | termcapinfo hp700 'Z0=\E[?3h:Z1=\E[?3l:hs:ts=\E[62"p\E[0$~\E[2$~\E[1$}:fs=\E[0}\E[61"p:ds=\E[62"p\E[1$~\E[61"p:ic@'
100 |
101 | # Extend the vt100 desciption by some sequences.
102 | termcap vt100* ms:AL=\E[%dL:DL=\E[%dM:UP=\E[%dA:DO=\E[%dB:LE=\E[%dD:RI=\E[%dC
103 | terminfo vt100* ms:AL=\E[%p1%dL:DL=\E[%p1%dM:UP=\E[%p1%dA:DO=\E[%p1%dB:LE=\E[%p1%dD:RI=\E[%p1%dC
104 |
105 |
106 | ################
107 | #
108 | # keybindings
109 | #
110 |
111 | #remove some stupid / dangerous key bindings
112 | bind k
113 | bind ^k
114 | bind .
115 | bind ^\
116 | bind \\
117 | bind ^h
118 | bind h
119 | #make them better
120 | bind 'K' kill
121 | bind 'I' login on
122 | bind 'O' login off
123 | bind '}' history
124 |
125 | # Yet another hack:
126 | # Prepend/append register [/] to the paste if ^a^] is pressed.
127 | # This lets me have autoindent mode in vi.
128 | register [ "\033:se noai\015a"
129 | register ] "\033:se ai\015a"
130 | bind ^] paste [.]
131 |
132 | ################
133 | #
134 | # default windows
135 | #
136 |
137 | # screen -t local 0
138 | # screen -t mail 1 elm
139 | # screen -t 40 2 rlogin faui40
140 |
141 | # caption always "%3n %t%? @%u%?%? [%h]%?"
142 | # hardstatus alwaysignore
143 | # hardstatus alwayslastline "%w"
144 |
145 |
--------------------------------------------------------------------------------
/elphel/globaldiagnostix.php:
--------------------------------------------------------------------------------
1 | $value) {
13 | $parameters[$key] = convert($value);
14 | }
15 |
16 | // parameters are set X frames in the future
17 | if (isset($_GET['framedelay']))
18 | {
19 | $framedelay = $_GET['framedelay'];
20 | }
21 | else
22 | $framedelay = 3; // default framedelay is 3
23 |
24 | function convert($s) {
25 | // clean up
26 | $s = trim($s, "\" ");
27 | // check if value is in HEX
28 | if(strtoupper(substr($s, 0, 2))=="0X")
29 | return intval(hexdec($s));
30 | else
31 | return intval($s);
32 | }
33 |
34 | /*
35 | get the current URL [http://is.gd/vllbf] and use
36 | echo print_r($_SERVER);
37 | to see all the variables available (as seen in the first comment on
38 | http://stackoverflow.com/a/189113/323100).
39 | */
40 |
41 | // Save CameraIP/phpfile into variable for re-use
42 | $url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'];
43 |
44 | // Give out some HTML code, so that we actually have a page to look at
45 | echo "\n\n GlobalDiagnostiX - PHP\n\n\n";
46 | echo "GlobalDiagnostiX exposure settings page
\n";
47 | echo "Image before doing anything
\n";
48 | echo "\n
\n
\n"; # go to the 'last' frame, go back '$framedelay +1' frames and show this 'img'. This should be the one image before setting anything (or one before that).
49 | echo "Image ".elphel_get_frame()." with";
50 | echo "- ";
51 | echo "Exposure time = ".(elphel_get_P_value(ELPHEL_EXPOS) / 1000)." msec";
52 | echo "
- ";
53 | echo "Auto exposure = ";
54 | if (elphel_get_P_value(ELPHEL_AUTOEXP_ON) == 1)
55 | echo "on";
56 | elseif (elphel_get_P_value(ELPHEL_AUTOEXP_ON) == 0)
57 | echo "off";
58 | echo "
";
59 | echo ".
\n";
60 |
61 | // only show parameters if we are actually setting anything, i.e when $parameters is not empty
62 | if ( !empty( $parameters ) )
63 | {
64 | echo "Setting parameters for camera
\n";
65 | echo "
\n";
66 | echo "The setting parameters from the URL are \n"; print_r($parameters); echo "
\n";
67 | }
68 | echo "
\n";
69 |
70 | if ( isset ( $parameters["exposure"] ) )
71 | {
72 | elphel_set_P_value(ELPHEL_AUTOEXP_ON,0); // turn off autoexposure if we're setting the exposure manually.
73 | elphel_set_P_value(ELPHEL_EXPOS,($parameters['exposure'] * 1000 )); // input is in msec, set is in usec
74 | }
75 |
76 | echo "Image after updated settings
\n";
77 | // wait for at least three frames for the setting from above to stick
78 | // it seems that wait_frame is not enough, we need to do skip_frames. Maybe this is because of the way PHP works...
79 | elphel_skip_frames($framedelay);
80 |
81 | echo "\n
\n
\n";
82 | echo "Image ".elphel_get_frame()." with";
83 | echo "- ";
84 | echo "Exposure time = ".(elphel_get_P_value(ELPHEL_EXPOS) / 1000)." msec";
85 | echo "
- ";
86 | echo "Auto exposure = ";
87 | if (elphel_get_P_value(ELPHEL_AUTOEXP_ON) == 1)
88 | echo "on";
89 | elseif (elphel_get_P_value(ELPHEL_AUTOEXP_ON) == 0)
90 | echo "off";
91 | echo "
";
92 | echo ".
\n";
93 |
94 | echo "
\n";
95 | /*
96 | echo "Click the links here to turn AE either 'ON' or 'OFF'.
\n";
97 | */
98 | echo "\n\n";
99 |
100 | // set autoexposure to what the user requested with "URL?autoexposure=bool"
101 | if ( isset ( $parameters["autoexposure"] ) )
102 | {
103 | elphel_set_P_value(ELPHEL_AUTOEXP_ON,$parameters["autoexposure"]);
104 | }
105 | elphel_skip_frames($framedelay);
106 |
107 | ?>
108 |
--------------------------------------------------------------------------------
/aptina/README.md:
--------------------------------------------------------------------------------
1 | # How to setup DevWareX on Ubuntu 12.04
2 |
3 | ## Install Python 3
4 | - Follow the [LinuxInstructions] from Aptina on their Atalassian-page, namely
5 | - `wget http://www.python.org/ftp/python/3.3.2/Python-3.3.2.tar.xz`
6 | - `tar -xvf Python-3.3.2.tar.xz`
7 | - `cd Python-3.3.2`
8 | - `./configure --enable-shared --prefix=/usr && make && make install`
9 | - `sudo ln -s /Library/Frameworks/Python.framework/Versions/3.3/Python /usr/lib/libpython3.3m.dylib`
10 |
11 | ## Install DevWare
12 | - `sudo apt-get install libtbb-dev` to install a necessary [library]
13 | - Download a recent version from the [DevSuite]-website, either manually or with this command, which downloads Version 1.9 for Linux32, unpacks it and starts the installation:
14 | `wget https://aptina.atlassian.net/wiki/download/attachments/11501573/DevWareX_Linux32_1_9.tar.gz;tar -xvf DevWare*.tar;./Developer`
15 |
16 | ## Get sensor and board files
17 | ### Very Easy
18 | - Get the files from your Aptina representative, save them to the `data` directory.
19 |
20 | ### Easy
21 | Since you've probably already checked out the [GlobalDiagnostiX repository][GDXrepo] or are working at PSI, you can just symlink the necessary files.
22 | - `cd` into the `data` directory inside the directory were you installed the DevSuite.
23 | - `ln -s /afs/psi.ch/project/EssentialMed/Dev/aptina/data/* .`
24 |
25 | ### Harder
26 | You can only check out the files you need from the [GlobaldiagnostiX repository][GDXrepo]. Although you probably want to go the *Very Easy* or *Easy route*...
27 | - `cd` into the directory were you installed the DevSuite.
28 | - `rm -r data` to remove the original `data` directory.
29 | - `git init data;cd data` to make a new Git repository.
30 | - `git remote add -f origin git@github.com:habi/GlobalDiagnostiX.git` to add the original repository as remote.
31 | - `git config core.sparsecheckout true;mkdir .git/info` to enable sparse checkout
32 | - `echo aptina/data/apps_data/ >> .git/info/sparse-checkout;echo aptina/data/board_data/ >> .git/info/sparse-checkout;echo aptina/data/sensor_data/ >> .git/info/sparse-checkout` to add the necessary files to the desired files to checkout.
33 | - `git pull origin master` to get them
34 | - `mv aptina/data/* .;rm -r aptina` to remove some cruft
35 |
36 | ## Start the DevSuite
37 | - `cd PATH_TO_DEVSUITE` and start it with `./DevWareX.exe`
38 |
39 | [LinuxInstructions]: https://aptina.atlassian.net/wiki/display/DEVS/DevWareX+Installation+Instructions+-+Linux
40 | [library]: http://packages.ubuntu.com/precise/libtbb-dev
41 | [DevSuite]: https://aptina.atlassian.net/wiki/display/DEVS/Software+Downloads
42 | [GDXrepo]: https://github.com/habi/GlobalDiagnostiX
43 |
44 | # How to setup DevWareX on Mac OS X
45 | ## Install Python 3
46 | - Follow the [MacInstructions] from Aptina on their Atalassian-page, namely
47 | - `cd; cd Downloads`
48 | - `wget https://www.python.org/ftp/python/3.3.2/python-3.3.2-macosx10.6.dmg; open python-3.3.2-macosx10.6.dmg`
49 | - Double-click on "Python.mpkg" and install Python 3.
50 | - `sudo ln -s /Library/Frameworks/Python.framework/Versions/3.3/Python /usr/lib/libpython3.3m.dylib`
51 |
52 | ## Install DevWare
53 | - `cd; cd Downloads`
54 | - `wget https://aptina.atlassian.net/wiki/download/attachments/11501573/DevWareX_MacOSX_1_8.dmg; open DevWareX_MacOSX_1_8.dmg`
55 | - Double-click on "Developer" ton install DevWare
56 | - `cd` into the `data` directory inside the directory were you installed the DevSuite.
57 |
58 | ## Get sensor and board files
59 | ### Very Easy
60 | - Get the files from your Aptina representative, save them to the `data` directory.
61 |
62 | ### Easy
63 | Since you've probably already checked out the [GlobalDiagnostiX repository][GDXrepo] or are working at PSI, you can just symlink the necessary files.
64 |
65 | - `cd` into the directory inside the directory were you installed the DevSuite, probably something like `cd /Applications/Aptina_DevWare`
66 | - If you're (always) connected to AFS, you can just enter `ln -s /afs/psi.ch/project/EssentialMed/Dev/aptina/data/* .`
67 | - Otherwise, if you're not connected to AFS, you just symlink the [data] directory of your checked out repository to the base directory of the DevWare installation.
68 |
69 | ## Start the DevSuite
70 | - `open /Applications/Aptina_DevWare/DevWareX.app/`, because you'll need the command-line output!
71 |
72 | You'll also need to associate the INI and SDAT files to "TextEditor" for various DevWareX features to work correctly:
73 |
74 | - Select any .ini file
75 | - `⌘+i` and select "Open With" and under "Applications" select "TextEdit".
76 | - Repeat these steps on any .xsdat file.
77 |
78 | [MacInstructions]: https://aptina.atlassian.net/wiki/display/DEVS/DevWareX+Installation+Instructions+-+MacOS
79 | [data]: https://github.com/habi/GlobalDiagnostiX/tree/master/aptina/data
80 |
81 |
--------------------------------------------------------------------------------
/SurfaceEntranceDose.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Script used to calculate the surface entrance dose of a certain x-ray
5 | measurement. Gives the same results as the 'Diagnostische Referenzwerte'
6 | Excel calculator on the BAG-page, in the right side-bar of http://is.gd/E2qIPA.
7 | The calculation is based on 'Merkblatt R-06-04' from BAG.
8 | """
9 |
10 | import numpy as np
11 | import matplotlib.pylab as plt
12 | from mpl_toolkits.mplot3d import Axes3D
13 |
14 | plt.ion()
15 |
16 | # Parameters
17 |
18 | # The K-value is based on the machine. The BAG-calculator (see below) list 0.1
19 | K = 0.1
20 | FocusDistance = 1.4
21 | # RSF as found by Arouna2000, cited by BAG2012. *This* RSF gives the same SED
22 | # values as the XLS-calculator from BAG (http://is.gd/oTpniQ) which I copied to
23 | # /afs/psi.ch/project/EssentialMed/PresentationsAndInfo/BAG/R-0 DRWCalc 5.0.xls
24 | RSF = 1.35
25 | # RSF as found in BAG2012. "Der über verschiedene Anlagen gemittelte
26 | # Korrekturfaktor betrug 1.15"
27 | # RSF = 1.15
28 |
29 | Range_kV = range(30, 130)
30 | Range_mAs = range(0, 60)
31 |
32 | # Using *bloody* list comprehension in Python as specified by
33 | # http://stackoverflow.com/a/2397150/323100 Why do I always forget this?
34 | Dose = np.asarray([[K *
35 | (float(kV) / 100) ** 2
36 | * mAs *
37 | (1 / FocusDistance) ** 2
38 | * RSF
39 | for kV in Range_kV] for mAs in Range_mAs])
40 |
41 | # Give out fixed data
42 | print '---'
43 | print 'The characteristic constant of the x-ray setup is', K, 'mGy/mA, the'
44 | print 'focus-surface-distance is', FocusDistance, 'm, the "Rückstreufaktor"'
45 | print 'RSF was set to', str(RSF) + '.'
46 | print
47 | print 'If we calculate the surface entrance dose'
48 | print 'SED = K*(U/100)^2*Q*(1/FOD)^2*RSF'
49 |
50 | # Give out plot and varying data
51 | # Surfaceplot based on http://stackoverflow.com/a/3812324/323100, since we only
52 | # have matplotlib 0.99.1.1 installed here at PSI. Check the installed version
53 | # with "python -c 'import matplotlib; print matplotlib.__version__'" in your
54 | # terminal.
55 | fig = plt.figure()
56 | ax = Axes3D(fig)
57 | X, Y = np.meshgrid(Range_kV, Range_mAs)
58 | ax.plot_surface(X, Y, Dose, cmap='jet', cstride=1, rstride=1, linewidth=0)
59 |
60 | showCase = 1
61 | if showCase == 1:
62 | # Multiple Values, including Zhentians Breast scan
63 | showkV = (46., 70., 120., 125., 40.)
64 | showmAs = (50., 2, 50., 2., 25.)
65 | what = ('Wrist 1', 'Wrist 2', 'LWS ap', 'Thorax', 'Zhentian')
66 | elif showCase == 2:
67 | # General values for radiology (from Heinz and R-06-04). Used in
68 | # movie in presentation
69 | showkV = (46., 70., 120., 125.)
70 | showmAs = (50., 2, 50., 2.)
71 | what = ('Wrist 1', 'Wrist 2', 'LWS ap', 'Thorax')
72 | elif showCase == 3:
73 | # The two wrist images in the talk, 70 kV with much too high mAs, but
74 | # otherwise we wouldn't have reached the exposure time needed to sync the
75 | # camera.
76 | showkV = (46., 70., 70.)
77 | showmAs = (50., 50, 2.)
78 | what = ('Wrist 1', 'Wrist 2 (Experiment)', 'Wrist 2 (Theory)')
79 |
80 | for kV, mAs, method in zip(showkV, showmAs, what):
81 | CurrentDose = Dose[Range_mAs.index(mAs)][Range_kV.index(kV)]
82 | ax.plot([kV], [mAs], CurrentDose, 'o', c='r', ms=20)
83 | label = '%d kV & %d mAs\nSE-Dose %0.3f mGy\n%s' % (kV, mAs, CurrentDose,
84 | method)
85 | if not showCase == 3:
86 | ax.text(kV, mAs, CurrentDose, label)
87 | print
88 | print 'For a radiography of a', method
89 | print 'with a source voltage of', kV, 'kV and a charge of', mAs, 'mAs, '
90 | print 'we get a SED of', round(CurrentDose, 3), 'mGy.'
91 |
92 | ax.set_xlabel('kV')
93 | ax.set_ylabel('mAs')
94 | ax.set_zlabel('Dose [mGy]')
95 |
96 | savemovie = False
97 | if savemovie:
98 | # Save output as movie: http://stackoverflow.com/a/12905458/323100
99 | angles = range(225 - 44, 225 + 44) # 150:270 good values for presentation
100 | counter = 1
101 | for angle in angles:
102 | ax.view_init(elev=34.4, azim=angle)
103 | print 'saving angle', str(angle + 1) + '° of 360° as movie' + \
104 | str("%03d" % angle) + '.png - [' + str(counter) + '/' +\
105 | str(len(angles)) + ']'
106 | plt.savefig('Dose_movie' + str("%03d" % angle) + '.png',
107 | transparent=False)
108 | plt.draw()
109 | counter += 1
110 | else:
111 | ax.view_init(elev=34.4, azim=225)
112 | plt.savefig('Dose' + str(showCase) + '.png', transparent=True)
113 |
114 | plt.ioff()
115 | print
116 | print 'done'
117 | plt.show()
118 |
--------------------------------------------------------------------------------
/GenerateTestImage.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Generate test-image for calculating the MTF
5 | """
6 | from __future__ import division
7 | import math
8 | import numpy
9 | import scipy.misc
10 | import itertools
11 | import matplotlib.pyplot as plt
12 |
13 |
14 | def draw_fansegment(startradius, length, angle):
15 | plt.arrow(Size[1] / 2 + (startradius * numpy.cos(numpy.deg2rad(angle))),
16 | Size[0] / 2 + (startradius * numpy.sin(numpy.deg2rad(angle))),
17 | length * numpy.cos(numpy.deg2rad(angle)),
18 | length * numpy.sin(numpy.deg2rad(angle)))
19 |
20 |
21 | def get_git_hash():
22 | """
23 | Get the current git hash from the repository.
24 | Good for saving this information into the log files of process.
25 | Based on http://stackoverflow.com/a/949391/323100 and
26 | http://stackoverflow.com/a/18283905/323100
27 | """
28 | from subprocess import Popen, PIPE
29 | import os
30 | gitprocess = Popen(['git', '--git-dir', os.path.join(os.getcwd(), '.git'),
31 | 'rev-parse', '--short', '--verify', 'HEAD'],
32 | stdout=PIPE)
33 | (output, _) = gitprocess.communicate()
34 | return output.strip()
35 |
36 | # Setup
37 | Size = [800, 600]
38 | numpy.random.seed(1796)
39 | FilePrefix = 'Phantom_' + get_git_hash() + '_'
40 |
41 | print 'Generating random image with a size of', Size[0], 'x', Size[1], 'px'
42 | # Generate random image
43 | ImageRandom = numpy.random.randint(2, size=Size) * 256
44 | scipy.misc.imsave(FilePrefix + 'random.png', ImageRandom)
45 | print 'Saved random image as', FilePrefix + 'random.png\n'
46 |
47 | # Write grid onto image
48 | GridSize = 100
49 | for x, y in itertools.izip_longest(range(0, Size[0], GridSize),
50 | range(0, Size[1], GridSize)):
51 | if x:
52 | ImageRandom[x, :] = 1
53 | if y:
54 | ImageRandom[:, y] = 1
55 | scipy.misc.imsave(FilePrefix + 'random_grid.png', ImageRandom)
56 | print 'Saved random image with grid as', FilePrefix + 'random_grid.png\n'
57 |
58 | # Checkerboard
59 | print 'Generating checkerboard pattern with a size of', Size[0], 'x', Size[1], \
60 | 'px'
61 | StripeSize = 50
62 | CheckerBoard = numpy.zeros(Size)
63 | # Horizontal stripes
64 | for x in range(Size[0]):
65 | if math.fmod(x, StripeSize * 2) < StripeSize:
66 | CheckerBoard[x, :] = 1
67 | # vertical stripes stipes -> flip vertical 0/1
68 | for y in range(Size[1]):
69 | if math.fmod(y, StripeSize * 2) < StripeSize:
70 | for x in range(Size[0]):
71 | if CheckerBoard[x, y]:
72 | CheckerBoard[x, y] = 0
73 | else:
74 | CheckerBoard[x, y] = 1
75 | CheckerBoard[:StripeSize, :] = 1
76 | CheckerBoard[-StripeSize:, :] = 1
77 | CheckerBoard[:, :StripeSize] = 1
78 | CheckerBoard[:, -StripeSize:] = 1
79 | scipy.misc.imsave(FilePrefix + 'checkerboard.png', CheckerBoard)
80 | print 'Saved checkerboard image as', FilePrefix + 'checkerboard.png\n'
81 |
82 | print 'Generating star pattern with a size of', Size[0], 'x', Size[1], 'px'
83 | # Draw star-pattern with defined length
84 | saveDPI = 150
85 | fig = plt.figure(figsize=(Size[1] / saveDPI, Size[0] / saveDPI))
86 | Length = 100
87 | angles = numpy.linspace(0, 360, 3600)
88 | for radius in range(0, max(Size), 2 * Length):
89 | for angle in angles:
90 | if round(angle / 10) % 2 == 0:
91 | draw_fansegment(radius, Length, angle)
92 | draw_fansegment(radius + Length, Length, angle + 10)
93 | plt.plot(Size[1] / 2 + radius * numpy.cos(numpy.deg2rad(angles)),
94 | Size[0] / 2 + radius * numpy.sin(numpy.deg2rad(angles)),
95 | color='k', linewidth=2)
96 | plt.plot(Size[1] / 2 + radius / 2 * numpy.cos(numpy.deg2rad(angles)),
97 | Size[0] / 2 + radius / 2 * numpy.sin(numpy.deg2rad(angles)),
98 | color='k', linewidth=2)
99 | plt.axis([0, Size[1], 0, Size[0]])
100 | plt.gca().axes.get_xaxis().set_visible(False)
101 | plt.gca().axes.get_yaxis().set_visible(False)
102 | fig.savefig(FilePrefix + 'star.png', dpi=saveDPI)
103 | plt.close()
104 | print 'Saved star pattern as', FilePrefix + 'star.png\n'
105 |
106 | # Show what we've done
107 | plt.figure('Result', figsize=(10, 10))
108 | plt.suptitle('Images from script version ' + get_git_hash())
109 | plt.subplot(221)
110 | plt.imshow(plt.imread(FilePrefix + 'random.png'), cmap='gray',
111 | interpolation='nearest')
112 | plt.title('Random image')
113 | plt.axis('off')
114 | plt.subplot(222)
115 | plt.imshow(plt.imread(FilePrefix + 'random_grid.png'), cmap='gray',
116 | interpolation='nearest')
117 | plt.title('with superimposed grid')
118 | plt.axis('off')
119 | plt.subplot(223)
120 | plt.imshow(plt.imread(FilePrefix + 'star.png'), cmap='gray',
121 | interpolation='nearest')
122 | plt.title('Poor mans Siemens star')
123 | plt.subplot(224)
124 | plt.axis('off')
125 | plt.imshow(plt.imread(FilePrefix + 'checkerboard.png'), cmap='gray',
126 | interpolation='nearest')
127 | plt.title('Checkerboard pattern')
128 | plt.axis('off')
129 | plt.show()
130 |
--------------------------------------------------------------------------------
/Analysis/functions.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Functions that we use for each and every script in the folder
5 | /afs/psi.ch/project/EssentialMed/Dev/Analysis
6 | """
7 |
8 |
9 | def AskUser(Blurb, Choices):
10 | """
11 | Ask for user input.
12 | Based on function in MasterThesisIvan.ini
13 | """
14 | print(Blurb)
15 | for Counter, Item in enumerate(sorted(Choices)):
16 | print ' * [' + str(Counter) + ']:', Item
17 | Selection = []
18 | while Selection not in range(len(Choices)):
19 | try:
20 | Selection = int(input(' '.join(['Please enter the choice you',
21 | 'want [0-' +
22 | str(len(Choices) - 1) +
23 | ']:'])))
24 | except SyntaxError:
25 | print 'You actually have to select *something*'
26 | if Selection not in range(len(Choices)):
27 | print 'Try again with a valid choice'
28 | print 'You selected', sorted(Choices)[Selection]
29 | return sorted(Choices)[Selection]
30 |
31 |
32 | def get_git_hash():
33 | """
34 | Get the current git hash from the repository.
35 | Good for saving this information into the log files of process.
36 | Based on http://stackoverflow.com/a/949391/323100 and
37 | http://stackoverflow.com/a/18283905/323100
38 | """
39 | from subprocess import Popen, PIPE
40 | import platform
41 |
42 | if platform.node() == 'anomalocaris':
43 | gitprocess = Popen(['git', '--git-dir',
44 | '/Volumes/slslc/EssentialMed/Dev/.git',
45 | 'rev-parse', '--short', '--verify', 'HEAD'],
46 | stdout=PIPE)
47 | else:
48 | gitprocess = Popen(['git', '--git-dir',
49 | '/afs/psi.ch/project/EssentialMed/Dev/.git',
50 | 'rev-parse', '--short', '--verify', 'HEAD'],
51 | stdout=PIPE)
52 | (output, _) = gitprocess.communicate()
53 | return output.strip()
54 |
55 |
56 | def myLogger(Folder, LogFileName):
57 | """
58 | Since logging in a loop does always write to the first instaniated file,
59 | we make a little wrapper around the logger function to write to a defined
60 | log file.
61 | Based on http://stackoverflow.com/a/2754216/323100
62 | """
63 | import logging
64 | import os
65 |
66 | logger = logging.getLogger(LogFileName)
67 | # either set INFO or DEBUG
68 | # ~ logger.setLevel(logging.DEBUG)
69 | logger.setLevel(logging.INFO)
70 | handler = logging.FileHandler(os.path.join(Folder, LogFileName), 'w')
71 | logger.addHandler(handler)
72 | return logger
73 |
74 |
75 | def get_experiment_list(StartingFolder):
76 | """
77 | Get all folders (Experiment) and ExperimentIDs inside StartingFolder
78 | """
79 | import os
80 | import platform
81 |
82 | if platform.node() != 'slslc06' and platform.node() != 'x02da-cons-2':
83 | from progressbar import ProgressBar, Percentage, Bar, ETA
84 |
85 | widgets = ['Reading: ', Percentage(), ' ', Bar(), ' ', ETA()]
86 | pbar = ProgressBar(widgets=widgets, maxval=5000).start()
87 | Experiment = []
88 | ExperimentID = []
89 | for root, dirs, files in os.walk(StartingFolder):
90 | if (len(os.path.basename(root)) == 7
91 | or len(os.path.basename(root)) == 8) \
92 | and not os.path.basename(root).startswith('2014') \
93 | and 'RECYCLE' not in os.path.basename(root) \
94 | and 'Pingseng' not in os.path.basename(root) \
95 | and 'Toshiba' not in os.path.basename(root) \
96 | and 'MT9' not in os.path.basename(root) \
97 | and 'AR0' not in os.path.basename(root):
98 | Experiment.append(root)
99 | ExperimentID.append(os.path.basename(root))
100 | if platform.node() != 'slslc06' and platform.node() != 'x02da-cons-2':
101 | pbar.update(len(ExperimentID))
102 | if platform.node() != 'slslc06' and platform.node() != 'x02da-cons-2':
103 | pbar.finish()
104 | return Experiment, ExperimentID
105 |
106 |
107 | def distance(Folder, chatty=False):
108 | import os
109 | import glob
110 |
111 | RawFileName = glob.glob(os.path.join(Folder, '*.raw'))[0]
112 | ScintillatorCMOSDistance = int(RawFileName.split('_')[5][:-5])
113 | if chatty:
114 | print 'Experiment', os.path.basename(Folder), \
115 | 'was performed with a scintillator-CMOS-distance of', \
116 | ScintillatorCMOSDistance, 'mm'
117 | return ScintillatorCMOSDistance
118 |
119 |
120 | def estimate_image_noise(image):
121 | """
122 | # Noise estimation according to http://stackoverflow.com/a/25436112/323100
123 | # based on Immerkaer1996
124 | """
125 | height, width = image.shape
126 | M = [[1, -2, 1],
127 | [-2, 4, -2],
128 | [1, -2, 1]]
129 | from scipy.signal import convolve2d
130 | import numpy as np
131 |
132 | sigma = np.sum(np.sum(np.absolute(convolve2d(image, M))))
133 | sigma = sigma * np.sqrt(0.5 * np.pi) / (6 * (width - 2) * (height - 2))
134 | return sigma
135 |
--------------------------------------------------------------------------------
/FocusFOVViewer.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Script to view the images from the lens-test (FOV and distance)
5 | The 'old' version from the first lenses was before git commit a100d19
6 |
7 | We load the RAW images in the folder for each sensor and give out some
8 | information on the images (distance, exposure time, etc) in a nice image
9 |
10 | The images were acquired with the INI file part "[Python: Focus-Distance-Test
11 | for Ivan]" of GDX.ini in DevWare
12 | """
13 |
14 | from __future__ import division
15 | import os
16 | import glob
17 | import numpy as np
18 | import matplotlib.pyplot as plt
19 | from matplotlib.offsetbox import OffsetImage, AnnotationBbox
20 |
21 | BaseDir = '/afs/psi.ch/project/EssentialMed/Images/Lens_FOV_and_Distance'
22 | # BaseDir = '/scratch/tmp/DevWareX/FocusDistance/'
23 |
24 | SensorList = []
25 | for item in os.listdir(BaseDir):
26 | if 'OldStuff' not in item:
27 | if not os.path.isfile(os.path.join(BaseDir, item)):
28 | SensorList.append(item)
29 |
30 | for Sensor in SensorList:
31 | print Sensor
32 | ImageFiles = sorted(glob.glob(os.path.join(BaseDir, Sensor, '*.raw')))
33 | Lens = [os.path.basename(item).split('_')[1] for item in ImageFiles]
34 | ImageSize = [[int(os.path.basename(item).split('_')[2].split('x')[1]),
35 | int(os.path.basename(item).split('_')[2].split('x')[0])]
36 | for item in ImageFiles]
37 | # The RAW files are "16-bit Unsigned" with the "Width" and "Height" in
38 | # "Little-Endian byte order"
39 | ImageData = [np.fromfile(item, dtype=np.uint16).reshape(
40 | ImageSize[ImageFiles.index(item)])
41 | for item in ImageFiles]
42 | SDD = [int(os.path.basename(item).split('_')[3].split('mm')[0])
43 | for item in ImageFiles]
44 | ExposureTime = [int(os.path.basename(item).split('_')[4].split('ms')[0])
45 | for item in ImageFiles]
46 | print 'SDD varies from', min(SDD), 'to', max(SDD), 'mm'
47 | print 'Exposure times vary from', min(ExposureTime), 'to',\
48 | max(ExposureTime), 'ms'
49 |
50 | # Display grid with images
51 | plt.figure(figsize=(16, 9))
52 | for counter, item in enumerate(ImageFiles):
53 | print str(counter + 1).zfill(len(str(len(ImageFiles)))) + '/' + \
54 | str(len(ImageFiles)) + '| Distance', str(SDD[counter]).rjust(3), \
55 | 'mm | Exp.', str(ExposureTime[counter]).rjust(2), 'ms | Lens', \
56 | Lens[counter]
57 | plt.subplot(3, int(np.ceil(len(ImageFiles) / 3)), counter + 1)
58 | # Show images
59 | plt.imshow(ImageData[counter], cmap=plt.cm.gray)
60 | # increase contrast
61 | # plt.imshow(ImageData[counter], cmap=plt.cm.gray, vmin=512,
62 | # vmax=rawimage.max() * 0.9)
63 | # draw contour
64 | # plt.contour(ImageData[counter], [rawimage.max() / 2])
65 | ImageTitle = str(Lens[counter]), '\nExp. time', \
66 | str(ExposureTime[counter]), 'ms, Dist.', str(SDD[counter]), 'mm'
67 | plt.title(' '.join(ImageTitle))
68 | plt.axis('off')
69 | plt.draw()
70 | # Save plot to an image we can view
71 | ImageOverview = os.path.join(BaseDir, Sensor + '_Images.png')
72 | plt.savefig(ImageOverview)
73 | print 'Overview image saved to', ImageOverview
74 | print 80 * '-'
75 |
76 | # Scatter plot sdd vs. exposure time with and without images, according to
77 | # http://matplotlib.org/examples/pylab_examples/demo_annotation_box.html
78 | # Plot the values with labels
79 | plt.figure(figsize=(16, 9))
80 | plt.subplot(121)
81 | plt.plot(SDD, ExposureTime, linestyle='', marker='o')
82 | for counter, item in enumerate(Lens):
83 | # Label data with lens name: http://stackoverflow.com/a/5147430/323100
84 | plt.annotate(item, xy=(SDD[counter],
85 | ExposureTime[counter] + np.random.random()),
86 | xytext=(0, 10), textcoords='offset points', ha='center',
87 | va='center', bbox=dict(boxstyle='round,pad=0.5', fc='b',
88 | alpha=0.125))
89 | plt.title(Sensor)
90 | plt.xlabel('Szintillator-Sensor-Distance [mm]')
91 | plt.ylabel('Exposure time [ms]')
92 | plt.grid(True)
93 | plt.xlim([0, max(SDD) * 1.1])
94 | plt.ylim([0, max(ExposureTime) * 1.1])
95 | # Scatter plot the images
96 | ax = plt.subplot(122)
97 | plt.plot(SDD, ExposureTime, linestyle='', marker='o')
98 | plt.title(Sensor)
99 | plt.xlabel('Szintillator-Sensor-Distance [mm]')
100 | plt.ylabel('Exposure time [ms]')
101 | plt.grid(True)
102 |
103 | ax = plt.subplot(122)
104 | for counter, item in enumerate(ImageFiles):
105 | imagebox = OffsetImage(ImageData[counter], zoom=0.1, cmap=plt.cm.gray)
106 | SubImage = AnnotationBbox(imagebox, [SDD[counter],
107 | ExposureTime[counter]], pad=0)
108 | ax.add_artist(SubImage)
109 | plt.xlim([0, max(SDD) * 1.1])
110 | plt.ylim([0, max(ExposureTime) * 1.1])
111 | plt.draw()
112 | ImageDistances = os.path.join(BaseDir, Sensor + '_Scatterplot.png')
113 | plt.savefig(ImageDistances)
114 | print 'Overview image saved to', ImageDistances
115 | print 80 * '-'
116 | plt.show()
117 |
--------------------------------------------------------------------------------
/FocusPlotLine.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Script to plot line on TIFF, useful for 'calculating' the best focus of
5 | the EssentialMed Setup. Since the objective doesn't really have the focal
6 | distance Edmund optics stated...
7 | Reads TIFF-Stack with images from focus shifting on the LINOS-rail. Plots
8 | a line (coordinates read from a txt-File, see around line 45) on the
9 | 'original' image and generates a lineplot from this line side by side
10 | with the slice. Opens fiji with all images in the end so we can easily
11 | browse through all those slices and find the best focus...
12 | """
13 |
14 | import libtiff
15 | import matplotlib.pylab as plt
16 | import linecache
17 | import os
18 |
19 | # Setup
20 | # All Apertures, "mixed media" on the intensifying screen
21 | # Series = 2
22 | # Only extreme Apertures, only Siemens-Star
23 | Series = 3
24 |
25 | # Leave '0' for 'natural' length of plot axis. Set to a value if scaling is
26 | # desired
27 | ScaleAxis = 600
28 |
29 | # Just plot a horizontal line in the middle of the images (or shifted by YSHIFT
30 | # below). If set to zero, Coordinates are read from file.
31 | SimpleHorizontalLine = 0
32 | # Shift this many pixels up from 1024, the middle of the image (Only used if
33 | # plotting SimpleHorizontalLine)
34 | YSHIFT = -50
35 |
36 | if Series == 2:
37 | Apertures = [1.8, 4, 8, 16]
38 | elif Series == 3:
39 | Apertures = [1.8, 16]
40 |
41 | # The trick with the [-X:] loads only the X last entries of the Aperture-List.
42 | for F in Apertures[-4:]:
43 | # Open tiff file
44 | tif = libtiff.TIFFfile('/afs/psi.ch/project/EssentialMed/Images/' +
45 | str(Series) + '-FocusTest/Series_F' +
46 | str('%04.1f' % F) + '.tif')
47 | # plt.ion()
48 | for i in range(0, len(tif.pages)):
49 | plt.figure(figsize=(16, 8))
50 | plt.subplots_adjust(left=None, bottom=None, right=None, top=None,
51 | wspace=None, hspace=None)
52 | # Add first subplot with Original Image and plot the line where we take
53 | # the lineprofile
54 | ax1 = plt.subplot(121)
55 | ax1.imshow(tif.asarray(key=i), cmap=plt.cm.gray)
56 | ax1.set_xlim([0, 2048])
57 | ax1.set_ylim([2048, 0])
58 | plt.axis('off')
59 | if SimpleHorizontalLine == 1:
60 | ax1.plot([0 + 400, 2048 - 400], [1024 - YSHIFT, 1024 - YSHIFT],
61 | 'r')
62 | # Cheat with 'line', so we have something to write at the end...
63 | line = 'asdf ' + str(0 + 400) + ' ' + str(1024 - YSHIFT) + ' ' + \
64 | str(2048 - 400) + ' ' + str(1024 - YSHIFT)
65 | else:
66 | # Reads coordinates from a text file which will then be used to
67 | # plot stuff on images.
68 | # '+2' since we have two headerlines '+1' since python starts at
69 | # line 0 :)
70 | line = linecache.getline('/afs/psi.ch/project/EssentialMed/Images/Coordinates_UpInStar.txt', i + 3)
71 | line = linecache.getline('/afs/psi.ch/project/EssentialMed/Images/Coordinates_Arbitrary.txt', i + 3)
72 | line = linecache.getline('/afs/psi.ch/project/EssentialMed/Images/Coordinates_LowerLine.txt', i + 3)
73 | ax1.plot([line.split()[1], line.split()[3]],
74 | [line.split()[2], line.split()[4]], 'r')
75 | plt.title('Image ' + str(i))
76 | # Plot Lineprofile
77 | ax2 = plt.subplot(122)
78 | if SimpleHorizontalLine == 1:
79 | ax2.plot(tif.asarray(key=i)[1024 - YSHIFT, 0 + 400:2048 - 400])
80 | else:
81 | ax2.plot(tif.asarray(key=i)[str(line.split()[2]),
82 | int(line.split()[1]):int(line.split()[3])])
83 | if ScaleAxis == 0:
84 | # scale x-axis of lineplot to real length
85 | ax2.set_xlim([0, (int(line.split()[3]) - int(line.split()[1]))])
86 | else:
87 | # scale x-axis of lineplot to the same length for all plots
88 | ax2.set_xlim([0, ScaleAxis])
89 | # Adapt to Brightness
90 | if F == 1.8:
91 | if Series == 2:
92 | ax2.set_ylim([0, 256])
93 | else:
94 | ax2.set_ylim([0, 256])
95 | # Only for Series 2...
96 | elif F == 4:
97 | ax2.set_ylim([0, 180])
98 | # Only for Series 2...
99 | elif F == 8:
100 | ax2.set_ylim([0, 250])
101 | elif F == 16:
102 | if Series == 2:
103 | ax2.set_ylim([0, 75])
104 | else:
105 | ax2.set_ylim([0, 256])
106 | plt.title('Line length: ' + str(int(line.split()[3]) -
107 | int(line.split()[1])))
108 | plt.draw()
109 | SaveName = '/afs/psi.ch/project/EssentialMed/Images/' + \
110 | str(Series) + '-FocusTest/F' + str('%04.1f' % F) + '/F' + \
111 | str('%04.1f' % F) + '_Image_' + str('%02d' % i) + \
112 | '_LineProfile_from_' + str(line.split()[1]) + '_to_' + \
113 | str(line.split()[3]) + '_on_height_' + str(line.split()[2]) + \
114 | '.png'
115 | plt.savefig(SaveName)
116 | print 'Figure ' + str(i) + ' saved to ' + SaveName
117 | plt.close()
118 |
119 | # View the figures we just saved as stack in Fiji
120 | viewcommand = '/scratch/Apps/Fiji.app/fiji-linux ' + \
121 | '/afs/psi.ch/project/EssentialMed/Images/' + str(Series) +\
122 | '-FocusTest/F' + str('%04.1f' % F) + '/F* &'
123 | os.system(viewcommand)
124 |
--------------------------------------------------------------------------------
/DetectorMockup/OmmatiDiag.scad:
--------------------------------------------------------------------------------
1 | // Mockup of OmmatiDiag
2 | /*
3 | * The mockup was done after the first crude drawing with
4 | * [Tinkercad](https://tinkercad.com/things/3utMiKsZhx9) proved to be too
5 | * inflexible.
6 | * The basic unit of the detector will be the scintillator size, which we
7 | * chose to be 17" x 17" translating to 430 x 430 mm in SI units.
8 | */
9 |
10 | // Generate screenshot on the Mac with
11 | // /Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD OmmatiDiag.scad --render --imgsize=1024,768 --projection=p -o Screenshot.png
12 | viewport_distance = 2000;
13 |
14 | // Basic length variables [mm]
15 | unitlength = 430;
16 | padding = 20;
17 | height = 100;
18 | overscan = 1.1;
19 | semitransparent = 0.618;
20 | nearlytransparent = 0.309;
21 |
22 | // dimlines.scad is used to easily draw dimensional measurements
23 | // http://www.cannymachines.com/entries/9/openscad_dimensioned_drawings
24 | include
25 |
26 | DIM_LINE_WIDTH = .025 * unitlength;
27 | DIM_SPACE = .1 * unitlength;
28 |
29 | module Scalebar()
30 | translate([unitlength + 100 , padding/2, 0]){
31 | rotate([0,0,90])
32 | dimensions(unitlength, line_width=DIM_LINE_WIDTH, loc=0);
33 | }
34 |
35 | // Housing
36 | module Housing()
37 | color ("gray", alpha=nearlytransparent) {
38 | // box bottom
39 | cube([unitlength + padding, unitlength + padding,1]);
40 | // box back walls
41 | cube([unitlength + padding, 1, height]);
42 | cube([1, unitlength + padding, height]);
43 | // box front walls, can (and should be turned off for increased visibility)
44 | translate(v = [0, unitlength + padding, 0]) {
45 | cube([unitlength + padding, 1, height]);
46 | }
47 | translate(v = [unitlength + padding, 0, 0]) {
48 | cube([1, unitlength + padding, height]); //
49 | }
50 | }
51 |
52 | // Scintillator
53 | module Scintillator()
54 | translate([padding/2, padding/2, 1]) {
55 | color ("green") {
56 | cube([unitlength,unitlength,1]);
57 | }
58 | }
59 |
60 | // CMOS Backplate
61 | module Backplate()
62 | translate([(unitlength+padding)/2 + 5, (unitlength+padding)/2 + 5, height-15]) {
63 | color ("red", alpha=semitransparent) {
64 | cube([350,320,1],center=true);
65 | }
66 | }
67 |
68 | // Ommatidium
69 | module Ommatidium() {
70 | // FOV of one Ommatidium
71 | FOVSize = ([unitlength/4, unitlength/3]);
72 | module FOV()
73 | color("red")
74 | cube([FOVSize[0] * overscan, FOVSize[1] * overscan, 1], center=true);
75 |
76 | // CMOS (Aptina AR0130)
77 | CMOSSize = ([1280, 960]);
78 | pixelsize = 3.75 / 1000; // [um]
79 | module CMOS()
80 | color ("blue")
81 | cube([CMOSSize[0] * pixelsize, CMOSSize[1] * pixelsize, 0.5], center=true);
82 | translate ([0,0,0]) CMOS();
83 |
84 | // Ommatidium back plate
85 | module Ommatidiumplate()
86 | color ("red")
87 | cube([15, 20, 1], center=true);
88 | translate ([5,7,0]) Ommatidiumplate();
89 |
90 | // FOV CMOS
91 | d_cmos_lens = 15;
92 | d_lens_scintillator = height - d_cmos_lens - 15;
93 | module CMOSCone()
94 | color("orange", alpha=nearlytransparent)
95 | cylinder(h = d_cmos_lens,
96 | r1 = CMOSSize[0] * pixelsize / 2 ,
97 | r2 = lensdiameter * 2, center=true);
98 | module CMOSCross() {
99 | color("blue", alpha=semitransparent)
100 | polyhedron(
101 | points=[ [0, 0, 0], //origin
102 | [-CMOSSize[0]*pixelsize/2, 0, d_cmos_lens], // horizontal_1
103 | [CMOSSize[0]*pixelsize/2, 0,d_cmos_lens], // horizontal_2
104 | [0, -CMOSSize[1] * pixelsize / 2,d_cmos_lens], // vertical_1
105 | [0, CMOSSize[1] * pixelsize / 2,d_cmos_lens]], // vertical_2
106 | faces=[[0,1,2], [0,3,4]]);
107 | }
108 |
109 | mirror([0,0,1]) translate([0,0,-d_cmos_lens])
110 | CMOSCross();
111 | translate([0,0,d_cmos_lens/2])
112 | CMOSCone();
113 |
114 | // Lens
115 | lensdiameter = 4;
116 | module Lens()
117 | // the lens is a squashed sphere: http://is.gd/4H9sZf
118 | scale([2,2,0.5]) sphere(lensdiameter, $fn=50, center=true);
119 | translate([0, 0, d_cmos_lens]) Lens();
120 |
121 | // FOV Lens
122 | module LensCone()
123 | color("orange", alpha=nearlytransparent)
124 | scale([1, 4/3, 1])
125 | cylinder(h = d_lens_scintillator,
126 | r1 = lensdiameter * 2,
127 | r2 = FOVSize[1]/2 * 1.2);
128 | module LensCross() {
129 | color("blue", alpha=semitransparent) polyhedron(
130 | points=[ [0, 0, 0], //origin
131 | [-FOVSize[0]/2 * overscan, 0, d_lens_scintillator], // horizontal_1
132 | [FOVSize[0]/2 * overscan, 0,d_lens_scintillator], // horizontal_2
133 | [0, -FOVSize[1]/2 * overscan ,d_lens_scintillator], // vertical_1
134 | [0, FOVSize[1]/2 * overscan,d_lens_scintillator]], // vertical_2
135 | faces=[[0,1,2], [0,3,4]]);
136 | }
137 |
138 | translate([0,0,d_cmos_lens])
139 | LensCross();
140 | translate([0,0,d_cmos_lens + d_lens_scintillator])
141 | FOV();
142 | translate([0,0,d_cmos_lens])
143 | LensCone();
144 |
145 | }
146 |
147 | rotate([0,0,$t*360])
148 | translate([-(unitlength+padding)/2,-(unitlength+padding)/2,0]){
149 | Scalebar();
150 | Scintillator();
151 | translate([unitlength/4/2 + padding/2, unitlength/3/2 + padding/2, height-14])
152 | mirror([0,0,1])
153 | for (xpos=[0:3], ypos = [0:2]) // iterate over x and y
154 | translate([xpos*unitlength/4, ypos*unitlength/3, 0])
155 | Ommatidium();
156 | Backplate();
157 | Housing();
158 | };
159 |
--------------------------------------------------------------------------------
/aptina/data/sensor_data/sensor_desc.xsl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Aptina Imaging -
8 |
9 |
10 |
11 |
12 |
13 | Aptina Imaging
14 |
15 |
16 |
Register/Bitfield Long Descriptions
17 |
Note that items displayed in Red indicate their contents are Confidential, items displayed in Black indicate their contents are NDA, and items displayed in Green indicate their contents are Public.
18 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | | Register Name |
34 | Long Description |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | |
122 |
123 |
124 | |
125 |
126 |
127 | |
128 |
129 |
130 |
131 |
132 |
133 |
134 | |
135 |
136 |
137 | |
138 |
139 |
140 | |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/MTF.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Script to calculate the Modulation transfer function of a edge target.
5 |
6 | Kickstarted from from https://gist.github.com/stefanv/2051954 and additional
7 | info from http://www.normankoren.com/Tutorials/MTF.html which tells us that
8 | "MTF can be defined as the magnitude of the Fourier transform of the point or
9 | line spread function. And some wikipedia lookup.
10 | """
11 |
12 | import numpy as np
13 | import scipy
14 | import scipy.ndimage
15 | import matplotlib.pylab as plt
16 | import time
17 |
18 |
19 | def MTF(edgespreadfunction):
20 | '''
21 | Compute the modulation transfer function (MTF).
22 |
23 | The MTF is defined as the FFT of the line spread function.
24 | The line spread function is defined as the derivative of the edge spread
25 | function. The edge spread function are the values along an edge, ideally a
26 | knife-edge test target. See an explanation here: http://is.gd/uSC5Ve
27 | '''
28 | linespreadfunction = np.diff(edgespreadfunction)
29 | return np.abs(np.fft.fft(linespreadfunction))
30 |
31 |
32 | def LSF(edgespreadfunction):
33 | '''
34 | Compute the modulation transfer function (MTF).
35 |
36 | The MTF is defined as the FFT of the line spread function.
37 | The line spread function is defined as the derivative of the edge spread
38 | function. The edge spread function are the values along an edge, ideally a
39 | knife-edge test target. See an explanation here: http://is.gd/uSC5Ve
40 | '''
41 | return np.abs(np.diff(edgespreadfunction))
42 |
43 |
44 | def polynomialfit(data, order):
45 | '''
46 | calculate the polynomial fit of an input for a defined degree
47 | '''
48 | x, y = range(len(data)), data
49 | coefficients = np.polyfit(x, y, order)
50 | return np.polyval(coefficients, x)
51 |
52 |
53 | # Generate edge for N points
54 | N = 250
55 | dirac = np.zeros(N)
56 | dirac[:N / 2] = 1
57 |
58 | # Filter edge
59 | sigma = [0.4, 0.6, 1]
60 | gauss_1 = scipy.ndimage.gaussian_filter(dirac, sigma=sigma[0])
61 | gauss_2 = scipy.ndimage.gaussian_filter(dirac, sigma=sigma[1])
62 | gauss_3 = scipy.ndimage.gaussian_filter(dirac, sigma=sigma[2])
63 |
64 | SaveFigure = False
65 | # Total = 55
66 | # for iteration in range(Total):
67 | # print 'Plotting', iteration, 'of', Total
68 |
69 | noise_sigma = 0.001
70 | gauss_1_noise = gauss_1 + noise_sigma * np.random.randn(len(dirac))
71 | gauss_2_noise = gauss_2 + noise_sigma * np.random.randn(len(dirac))
72 | gauss_3_noise = gauss_3 + noise_sigma * np.random.randn(len(dirac))
73 |
74 |
75 | '''
76 | Save the plots in a dictionary, so we can iterate through it afterwards. See
77 | http://stackoverflow.com/a/2553532/323100 and http://is.gd/d008ai for reference
78 | '''
79 | plots = dict((name, eval(name)) for name in ['dirac',
80 | 'gauss_1', 'gauss_1_noise',
81 | 'gauss_2', 'gauss_2_noise',
82 | 'gauss_3', 'gauss_3_noise'])
83 |
84 | plt.figure(figsize=(16, 16))
85 | counter = 0
86 | ShowRegion = 10
87 | for name, data in sorted(plots.iteritems()):
88 | counter += 1
89 | plt.subplot(4, len(plots), counter)
90 | plt.plot(data)
91 | plt.ylim(-0.1, 1.1)
92 | # plt.xlim(len(dirac) / 2 - ShowRegion / 2, len(dirac) / 2 + ShowRegion /
93 | # 2)
94 | if name == 'dirac':
95 | plt.ylabel('Edge response')
96 | plt.title(name)
97 | if name == 'gauss_1':
98 | plt.title(name + '\nSigma=' + str(sigma[0]))
99 | if name == 'gauss_2':
100 | plt.title(name + '\nSigma=' + str(sigma[1]))
101 | if name == 'gauss_3':
102 | plt.title(name + '\nSigma=' + str(sigma[2]))
103 |
104 | plt.subplot(4, len(plots), counter + len(plots))
105 | plt.plot(LSF(data))
106 | plt.ylim(-0.1, 1.1)
107 | if name == 'dirac':
108 | plt.ylabel('Edge response')
109 |
110 | plt.subplot(4, len(plots), counter + 2 * len(plots))
111 | plt.plot(MTF(data))
112 | plt.plot(np.ones(N) * MTF(data)[len(dirac) / 2])
113 | plt.ylim(-0.1, 1.1)
114 | plt.xlim(0, len(dirac) / 2)
115 | if name == 'dirac':
116 | plt.ylabel('MTF @ Nyquist')
117 | plt.text(0.618 * len(dirac) / 2, MTF(data)[len(dirac) / 2] - 0.1,
118 | ' '.join([str(np.round(MTF(data)[len(dirac) / 2], 3) * 100),
119 | '%']),
120 | fontsize=12, backgroundcolor='w')
121 |
122 | plt.subplot(4, len(plots), counter + 3 * len(plots))
123 | plt.plot(MTF(data), label='orig')
124 | # for degree in range(10,25):
125 | # plt.plot(polynomialfit(MTF(data), degree), label=str(degree))
126 | # plt.legend()
127 | degree = 4
128 | plt.plot(polynomialfit(MTF(data), degree), label=str(degree), color='r')
129 | plt.plot(np.ones(N) * polynomialfit(MTF(data), degree)[len(dirac) / 2],
130 | color='g')
131 | plt.ylim(-0.1, 1.1)
132 | plt.xlim(0, len(dirac) / 2)
133 | if name == 'dirac':
134 | plt.ylabel(' '.join(['polynomial fit of order', str(degree),
135 | '\nfitted MTF @ Nyquist']))
136 | plt.text(0.618 * len(dirac) / 2, MTF(data)[len(dirac) / 2] - 0.1,
137 | ' '.join([str(np.round(polynomialfit(MTF(data),
138 | degree)[len(dirac) / 2],
139 | 3) * 100), '%']),
140 | fontsize=12, backgroundcolor='w')
141 |
142 | plt.subplot(4, len(plots), 1)
143 | plt.plot(dirac, 'b')
144 | plt.ylim(-0.1, 1.1)
145 | plt.axvspan(len(dirac) / 2 - ShowRegion / 2, len(dirac) / 2 + ShowRegion / 2,
146 | facecolor='r', alpha=0.5)
147 | plt.title('Ideal knife edge\n red zoom-region\n is shown right')
148 |
149 | if SaveFigure:
150 | plt.savefig('MTF_' + str(int(time.time() * 10)) + '.png')
151 | else:
152 | plt.show()
153 |
--------------------------------------------------------------------------------
/Analysis/DetectWhichImageIsFocusedBest.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf8 -*-
2 |
3 | """
4 | Script to load a bunch of images and caculate the focus of them, based on the
5 | standard deviation of the gray values. The image with the smallest standard
6 | deviation (from the same scene!) should be the one focused best.
7 | """
8 |
9 | from __future__ import division
10 | import glob
11 | import os
12 | import matplotlib.pyplot as plt
13 | import sys
14 | import numpy
15 |
16 | # Setup
17 | Root = '/afs/psi.ch/project/EssentialMed/Images/13-Aptina_Focus_Test/'
18 | # Root = '/Volumes/WINDOWS'
19 | # Root = '/scratch/tmp/DevWareX/AR0130'
20 | # Root = '/scratch/tmp/DevWareX/A-1300'
21 |
22 | Directories = sorted([x[0] for x in os.walk(Root)][1:])
23 |
24 | print 'I found these folders in', Root
25 | print 'Which folder do you want to analyze?'
26 | for item in enumerate(Directories):
27 | print str(item[0]) + ':', os.path.basename(item[1]), 'containing'
28 | print ' ', len(glob.glob(os.path.join(item[1], '*.raw'))), \
29 | 'raw and'
30 | print ' ', len(glob.glob(os.path.join(item[1], '*.png'))), 'png files'
31 |
32 | ChosenFolder = []
33 | while ChosenFolder not in range(len(Directories)):
34 | ChosenFolder = int(raw_input('Enter [0:' + str(len(Directories) - 1) +
35 | ']: '))
36 |
37 | # Get list of files. These are the images we acquired of which
38 | # one has the best focus
39 | Images = sorted(glob.glob(os.path.join(Directories[ChosenFolder], '*.png')))
40 | if not Images:
41 | print 'I cannot find any png images in', \
42 | os.path.basename(Directories[ChosenFolder])
43 | print 'Did you save the images as RAW and not convert them yet?'
44 | sys.exit('Please try again...')
45 | else:
46 | print 'I will work with the', \
47 | len(glob.glob(os.path.join(Directories[ChosenFolder], '*.png'))),\
48 | 'png files in', Directories[ChosenFolder]
49 | # Discard some images
50 | Images = Images[3:]
51 |
52 | print
53 | print 'I will use', len(Images), '*.png images in', Directories[ChosenFolder]
54 |
55 | # Iterate through the files, calculate the standard deviation of each image and
56 | # plot this.
57 | print 'Calculating Mean of each of', len(Images), 'images'
58 | MeanExposure = [numpy.mean(plt.imread(x)) for x in Images]
59 |
60 | print 'Calculating standard deviation of each of', len(Images), 'of images'
61 | STD = [numpy.std(plt.imread(x)) for x in Images]
62 |
63 | normalize = True
64 | if normalize:
65 | # Normalize the values around the mean and convert the now array back to a
66 | # list
67 | MeanExposure = MeanExposure - numpy.mean(MeanExposure)
68 | MeanExposure = MeanExposure.tolist()
69 | STD = STD - numpy.mean(STD)
70 | STD = STD.tolist()
71 |
72 | plt.figure(figsize=(16, 9))
73 | plt.subplot(311)
74 | plt.title(' '.join([str(len(Images)), 'Images read from',
75 | os.path.basename(Directories[ChosenFolder])]))
76 | # Plot values
77 | plt.plot(MeanExposure, color='r', alpha=0.5,
78 | label='Exposure with Max @ Img. ' +
79 | str(MeanExposure.index(max(MeanExposure))))
80 | plt.plot(STD, color='b', alpha=0.5,
81 | label='STD with Max @ Img. ' + str(STD.index(max(STD))))
82 | # Print details and plot positions of 'Details' chosen images
83 | Details = 10
84 | for i in range(1, len(Images), int(round(len(Images) / Details))):
85 | print str(i).zfill(2), '|',
86 | if normalize:
87 | print 'normalized',
88 | print 'Exp', str(round(MeanExposure[i], 4)), '|',
89 | if normalize:
90 | print 'normalized',
91 | print 'STD of', round(STD[i], 4)
92 | plt.plot(i, MeanExposure[i], color='r', marker='.')
93 | plt.plot(i, STD[i], color='b', marker='.')
94 | plt.annotate(i, xy=(i, STD[i]), xytext=(0, 15),
95 | textcoords='offset points', ha='center', va='top')
96 | # Plot and mark worst and best image: http://stackoverflow.com/a/5147430/323100
97 | plt.plot(STD.index(min(STD)), min(STD), color='b', marker='v')
98 | plt.annotate(os.path.basename(Images[STD.index(min(STD))]),
99 | xy=(STD.index(min(STD)), min(STD)), xytext=(0, 30),
100 | textcoords='offset points', ha='center', va='bottom',
101 | bbox=dict(boxstyle='round,pad=0.5', fc='b', alpha=0.125),
102 | arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0.79'))
103 | plt.plot(STD.index(max(STD)), max(STD), color='b', marker='^')
104 | plt.annotate(os.path.basename(Images[STD.index(max(STD))]),
105 | xy=(STD.index(max(STD)), max(STD)), xytext=(0, 30),
106 | textcoords='offset points', ha='center', va='bottom',
107 | bbox=dict(boxstyle='round,pad=0.5', fc='b', alpha=0.125),
108 | arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0.79'))
109 | plt.xlim([0, len(Images) - 1])
110 | plt.legend(loc='best')
111 | print
112 | print 'Image', str(MeanExposure.index(max(MeanExposure))), '(' + \
113 | os.path.basename(Images[MeanExposure.index(max(MeanExposure))]) +\
114 | ') is the brightest.'
115 | print 'Image', str(STD.index(max(STD))), '(' + \
116 | os.path.basename(Images[STD.index(max(STD))]) + ') has the largest STD.'
117 | print
118 | print 'Use Image'
119 | print Images[STD.index(max(STD))]
120 | print 'for further tests'
121 |
122 | # Display selection of images
123 | Counter = Details + 1
124 | for i in range(1, len(Images), int(round(len(Images) / Details))):
125 | plt.subplot(3, 10, Counter)
126 | plt.imshow(plt.imread(Images[i]), cmap='gray')
127 | # plt.title(os.path.basename(Images[i]))
128 | plt.title('Img ' + str(i))
129 | Counter += 1
130 | # Display worst and best image
131 | plt.subplot(3, 2, 5)
132 | plt.imshow(plt.imread(Images[STD.index(min(STD))]), cmap='gray')
133 | plt.title('worst@' + os.path.basename(Images[STD.index(min(STD))]))
134 | plt.subplot(3, 2, 6)
135 | plt.imshow(plt.imread(Images[STD.index(max(STD))]), cmap='gray')
136 | plt.title('best@' + os.path.basename(Images[STD.index(max(STD))]))
137 | plt.savefig(Directories[ChosenFolder] + '.png')
138 | plt.show()
139 |
--------------------------------------------------------------------------------
/SetupPi.md:
--------------------------------------------------------------------------------
1 | # Raspberry Pi setup from scratch
2 | I plugged in a cheap 4 GB SD card I just had lying around, brought the system up and running and cobbled together bits and pieces and got a working system.
3 | This document here describes *all* the necessary steps to get up and runnig with a virgin SD card.
4 | If you follow all the steps in this document, you should have a RPi ready to use for the [GlobalDiagnostiX](http://globaldiagnostix.org) project.
5 | The aim is to be able to interact with several cameras ([Elphel](http://elphel.com), [Awaiba](http://www.awaiba.com/), [The Imaging Source](http://www.theimagingsource.com/)) and to acquire images from a scintillator screen using a (commercial) x-ray source.
6 |
7 | ## Prerequisites
8 | - According to the [Embedded Linux Wiki](http://elinux.org/RPi_SD_cards), a Transcend 16GB SDHC card is working well. Order one from [Digitec](https://www.digitec.ch/ProdukteDetails2.aspx?Reiter=Details&Artikel=194092) for (currently) 24 CHF.
9 | - Download the [BerryBoot Installer](http://www.berryterminal.com/doku.php/berryboot) and unzip it onto your SD card
10 | - Boot your Raspberry Pi from this SD card to install a current version of [Raspbian](http://www.raspbian.org/) or any other operating system.
11 | - Reboot. This will then go through `raspi-config` to reconfigure locales, keyboard and timezone (if necessary)
12 | - `sudo /etc/ntp.conf` to add the timeservers of PSI to the configuration, so we have the correct time. Add the lines below as first `server` entries
13 | # Permit time synchronization with our time source, but do not
14 | # permit the source to query or modify the service on this system.
15 | server pstime1.psi.ch
16 | restrict pstime1.psi.ch mask 255.255.255.255 nomodify notrap noquery
17 | server pstime2.psi.ch
18 | restrict pstime2.psi.ch mask 255.255.255.255 nomodify notrap noquery
19 | server pstime3.psi.ch
20 | restrict pstime3.psi.ch mask 255.255.255.255 nomodify notrap noquery
21 |
22 | ## Further setup
23 | - Update the repositories and upgrade the system to the newest packages with `sudo apt-get update && sudo apt-get upgrade`.
24 | This will will take a *long* time
25 | - Reboot
26 | - Install git and pull the GlobalDiagnostiX repository into your home folder.
27 | This should also take care of the `git` configuration, since we're also pulling the `.gitconfig` from the repo.
28 |
29 | sudo apt-get install git
30 | cd
31 | sudo rm -r * # to remove *EVERYTHING* from your home directory
32 | sudo rm -r .* # REALLY, EVERYTHING!
33 | git clone https://github.com/habi/GlobalDiagnostiX.git ~ # clone the GitHub repo into your home directory
34 | nano SetupPi.md # change something in the file
35 | git commit -a;git push # commit the change and push it back to the repo to see if that works
36 |
37 | - Install other packages with the lines below. This will take either take a *very* long time or go quite quickly if the command above took long. Anways, go and have a coffe....
38 | You can either do it line by line (below, with explanations) or just copy the oneliner at the bottom which does everything in one go.
39 |
40 | sudo apt-get install libblas-dev # good for scipy and numpy, see also http://raspberrypi.stackexchange.com/a/1730
41 | sudo apt-get install liblapack-dev # ditto
42 | sudo apt-get install python-dev # we want to develop in python
43 | sudo apt-get install libatlas-base-dev # speeds up execution according to http://is.gd/H7zqxv
44 | sudo apt-get install gfortran # compiler for scipy and numpy
45 | sudo apt-get install python-setuptools # helps with download, build and installation of Python packages
46 | sudo apt-get install python-scipy # install scipy
47 | sudo apt-get install python-numpy # install numpy
48 | sudo apt-get install python-matplotlib # no plotting without it
49 | sudo apt-get install ipython # interactive Pythoning
50 | sudo apt-get install geany # my preferred Python IDE
51 | sudo apt-get install imagemagick # do some image magic
52 | sudo apt-get install imagej # view and work with images
53 | sudo apt-get install chromium-browser # faster than Midori according to http://is.gd/8Hgfcc
54 | sudo apt-get install screen # Nice terminal multiplexer
55 | sudo apt-get install telnet # so we can `telnet` to the Elphel-camera as 'root'/'pass'
56 | sudo apt-get install ftp # so we can `ftp`stuff to the Elphel-camera
57 | sudo apt-get install gftp # so we can graphically `ftp`stuff to the Elphel-camera
58 | sudo apt-get install gedit # graphical text editor
59 | sudo apt-get install tkdiff # a graphical tool to diff files
60 | sudo apt-get isntall mplayer vlc # install two movie players, to look at camera output
61 |
62 | Here's all of the above as a oneliner:
63 |
64 | sudo apt-get install libblas-dev liblapack-dev python-dev libatlas-base-dev gfortran python-setuptools python-scipy python-numpy python-matplotlib ipython geany imagemagick openjdk-6-jre openjdk-6-jdk imagej chromium-browser screen telnet ftp gftp gedit tkdiff mplayer vlc
65 |
66 | # Tweaks
67 | - Num Lock on boot (according to [RPi-forum](http://is.gd/Fa0DxF)
68 | - `sudo nano /etc/kbd/config`
69 | - CTRL+V two or three times to go to line 67 (nearly at the bottom)
70 | - remove the comment in front of "LEDS=+num"
71 | - Install and set up `pep8`
72 | - `sudo easy_install pep8`
73 | - Open Geany and open the *Build* > *Set Build Commands* menu
74 | - For *Independend commands* Nr. 2, paste `pep8 %f` and for *Error regular expression* add `([^:]+):([0-9]+):([0-9:]+)? .*
75 | ` (adapted from [the Geany wiki](http://wiki.geany.org/howtos/check_python_code)).
76 | - Buy a small monitor
77 | - I bought http://bit.ly/10N9MbN and used a 12V power supply we had at TOMCAT.
78 | - Setup the composite (yellow) output to support the resolution of the monitor (PAL or NTSC, 480x272)
79 | - Go to 'Edit menu' in the BerryBoot boot menu, click on the arrow on top right to go to 'Advanced configuration' and edit the config.txt. See [this post in the RPI forum](http://raspberrypi.org/phpBB3//viewtopic.php?f=26&t=16403) for a screenshot.
80 | - add `framebuffer_width=480` and `sdtv_aspect=3`, according to the [Video mode options](http://elinux.org/RPiconfig#Video_mode_options)
81 | - Reboot with 'Exit'
82 | - Have fun!
83 |
--------------------------------------------------------------------------------
/Demonstrator/undistort_images.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Script to undistort camera images, based on checkerboard pattern.
5 | The script is based on [the OpenCV-Python tutorials](http://is.gd/KhTOuX) and
6 | the [OpenCV Python samples](http://git.io/dydjBQ)
7 |
8 | The script *needs* the example images left*.jpg found in opencv/samples/cpp or
9 | available for download [here](http://git.io/MDUBRw).
10 | """
11 |
12 | import os
13 | import numpy
14 | import cv2
15 | import glob
16 | import matplotlib.pylab as plt
17 | from scipy import ndimage
18 |
19 | LoadOmmatidiag = True
20 |
21 | if LoadOmmatidiag:
22 | BaseDir = '/afs/psi.ch/project/EssentialMed/Images' \
23 | '/DetectorElectronicsTests/EssentialLab/1421142758_checkerboard'
24 | OriginalsList = glob.glob(os.path.join(BaseDir, 'data-e*-g*-i*-??.png'))
25 | ImageList = OriginalsList
26 | else:
27 | BaseDir = '/afs/psi.ch/project/EssentialMed/Dev/Demonstrator'
28 | ImageList = glob.glob(os.path.join(BaseDir, 'left*.jpg'))
29 | OriginalsList = ImageList
30 |
31 | # termination criteria
32 | criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 50, 0.001)
33 |
34 | # We *have* to give the pattern size to look for.
35 | # This is the number of visible chess board "inner edges", i.e. number of
36 | # rows and columns -1.
37 | # For the sample images (LoadOmmatidiag = False) (7,6) is good.
38 | # For the edited checkerboard images, we use (5, 4).
39 | # See http://dasl.mem.drexel.edu/~noahKuntz/openCVTut10.html for counting :)
40 | if LoadOmmatidiag:
41 | PatternSize = (9, 5)
42 | # PatternSize = (3, 3)
43 | else:
44 | PatternSize = (7, 6)
45 | # Prepare object points, like (0,0,0), (1,0,0), (2,0,0) ...., (6,5,0)
46 | ObjectPoints = numpy.zeros((PatternSize[1] * PatternSize[0], 3), numpy.float32)
47 | ObjectPoints[:, :2] = numpy.mgrid[0:PatternSize[0],
48 | 0:PatternSize[1]].T.reshape(-1, 2)
49 |
50 | # Arrays to store object points and image points from all the images.
51 | # 3d point in real world space
52 | RealWorldPoints = []
53 | # 2d points in image plane.
54 | ImagePoints = []
55 |
56 | if not ImageList:
57 | print 'Download left*.jpg from http://git.io/MDUBRw and save these', \
58 | 'images in the same folder as this script'
59 | exit('FilesNotFound')
60 |
61 | plt.figure('Original images', figsize=[12, 11])
62 | for counter, FileName in enumerate(ImageList):
63 | print 'processing %s...' % FileName
64 | Image = cv2.imread(FileName)
65 | # Image = cv2.GaussianBlur(Image, (11, 11), 0)
66 | Image_BW = cv2.cvtColor(Image, cv2.COLOR_BGR2GRAY)
67 |
68 | # Find the chess board corners.
69 | Found, Corners = cv2.findChessboardCorners(Image_BW, PatternSize)
70 |
71 | # If found, refine the image points, add them to their lists and draw
72 | # the chessboard corners on the images
73 | if Found:
74 | print 'Found pattern!'
75 | # Find more precise corner points. The fist tuple influences the side
76 | # length of the search window. The second tuple is the dead region in
77 | # the middle of the search zone, see http://is.gd/xm6SXi
78 | cv2.cornerSubPix(Image_BW, Corners, (30, 30), (-1, -1), criteria)
79 | ImagePoints.append(Corners)
80 | RealWorldPoints.append(ObjectPoints)
81 | cv2.drawChessboardCorners(Image, PatternSize, Corners, Found)
82 | plt.imsave(FileName[:-4] + '_pattern.png', Image)
83 | print 'Saving found pattern image as %s' % FileName[:-4] + '_pattern.png'
84 | plt.subplot(3, 4, 11 - counter + 1)
85 | plt.imshow(ndimage.rotate(Image, 270), cmap='gray',
86 | interpolation='nearest')
87 | ImageTitle = os.path.basename(FileName)
88 | if Found:
89 | ImageTitle = '\n'.join(('Pattern found on', ImageTitle))
90 | plt.title(ImageTitle)
91 | plt.axis('off')
92 |
93 | if not ImagePoints:
94 | print 'I was not able to find a pattern on any image, maybe try ' \
95 | 'another "PatternSize"...'
96 | exit('PatternNotFound')
97 |
98 | print '\nApplying undistortion parameters found on %s images to %s ' \
99 | 'Orignals\n' % (len(ImagePoints), len(OriginalsList))
100 |
101 | # Calibrate camera if points are found
102 | RMS, CameraMatrix, DistortionCoefficients, rvecs, tvecs = \
103 | cv2.calibrateCamera(RealWorldPoints, ImagePoints, Image_BW.shape, None,
104 | None)
105 |
106 | # Display images
107 | plt.figure('Undistorted images', figsize=[12, 11.2])
108 | for counter, FileName in enumerate(OriginalsList):
109 | print 'undistorting %s...' % FileName
110 | Image = cv2.imread(FileName)
111 | NewCameraMatrix, ROI = \
112 | cv2.getOptimalNewCameraMatrix(CameraMatrix, DistortionCoefficients,
113 | (Image.shape[1], Image.shape[0]), 1,
114 | (Image.shape[1], Image.shape[0]))
115 |
116 | # Undistort images
117 | UndistortedImage = cv2.undistort(Image, CameraMatrix,
118 | DistortionCoefficients, None,
119 | NewCameraMatrix)
120 | plt.imsave(FileName[:-4] + '_undistort.png', UndistortedImage)
121 |
122 | # The images are rotated in respect to the "viewing plane" of the user,
123 | # we thus plot them in the "from bottom right to top left" order
124 | plt.subplot(3, 4, 11 - counter + 1)
125 | # and to show them "correctly", we need to rotate the images 270°
126 | clip = 65
127 | plt.imshow(ndimage.rotate(UndistortedImage[clip:-clip, clip:-clip], 270),
128 | cmap='gray', interpolation='nearest')
129 | plt.axis('off')
130 | plt.gca().text(512, 640, FileName.split('-')[-1].split('.')[0],
131 | verticalalignment='center', horizontalalignment='center',
132 | bbox={'facecolor': 'white', 'alpha': 0.5, 'pad': 10})
133 | plt.subplots_adjust(wspace=0, hspace=0)
134 | plt.suptitle(' '.join(['Undistorted images, clipped by %s pixels on each '
135 | 'side' % clip]))
136 | plt.axis('off')
137 |
138 | print "RMS:", RMS
139 | print "camera matrix:\n\t", CameraMatrix
140 | print "distortion coefficients: ", DistortionCoefficients
141 | print 'Checkerboard pattern found on', len(ImagePoints), 'images'
142 |
143 | plt.show()
144 |
--------------------------------------------------------------------------------
/aptina/NoiseVsExposure.py:
--------------------------------------------------------------------------------
1 | """
2 | Script to visualize the "Noise vs. Exposure" data from DevWare
3 |
4 | According to section 2.2.4.7 of the DevWareX help (http://is.gd/mt7FyF) we get
5 | signal levels and different kinds of noise measurements by using the "Sensor
6 | Control" > "Diagnostics" > "Noise vs. Exposure" tool.
7 |
8 | The analyis report gives
9 | - Signal
10 | - RMS Dyn (temporal noise), Avg Dyn (temporal noise)
11 | - FPN (fixed pattern noise), Col FPN (columnwise FPN), Row FPN (rowwise FPN)
12 | - Col Dyn (columnwise temporal noise) and Row Dyn (rowwise temporal noise).
13 |
14 | See the wiki page linkes above to see how the values are calulated.
15 | """
16 |
17 | import glob
18 | import os
19 | import numpy
20 | import matplotlib.pyplot as plt
21 |
22 |
23 | def AskUser(Blurb, Choices):
24 | """ Ask for input. Based on function in MasterThesisIvan.ini """
25 | print(Blurb)
26 | for Counter, Item in enumerate(sorted(Choices)):
27 | print ' * [' + str(Counter) + ']:', Item
28 | Selection = []
29 | while Selection not in range(len(Choices)):
30 | try:
31 | Selection = int(input(' '.join(['Please enter the choice you',
32 | 'want [0-' +
33 | str(len(Choices) - 1) +
34 | ']:'])))
35 | except SyntaxError:
36 | print 'You actually have to select *something*'
37 | if Selection not in range(len(Choices)):
38 | print 'Try again with a valid choice'
39 | print 'You selected', sorted(Choices)[Selection]
40 | return sorted(Choices)[Selection]
41 |
42 |
43 | Root = '/afs/psi.ch/project/EssentialMed/Images/NoiseVsExposure'
44 | DataFiles = [os.path.basename(i) for i in
45 | glob.glob(os.path.join(Root, '*.txt'))]
46 |
47 | # Which plot do we show?
48 | whichone = DataFiles.index(AskUser('Which file should I show you?', DataFiles))
49 | # If no manual selection, we can do
50 | # for whichone in range(len(DataFiles)):
51 | # in a loop...
52 |
53 | # Tell what we do
54 | Sensor = DataFiles[whichone][:-4].split('_')[0]
55 | Lens = DataFiles[whichone][:-4].split('_')[1]
56 | FramesPerSample = DataFiles[whichone][:-4].split('_')[2]
57 | MaximumExposure = DataFiles[whichone][:-4].split('_')[3]
58 | Decades = DataFiles[whichone][:-4].split('_')[4]
59 | SamplesPerDecade = DataFiles[whichone][:-4].split('_')[5]
60 |
61 | print 'We are showing the data from the', Sensor, 'CMOS with the', Lens, \
62 | 'lens. The analysis was done with', FramesPerSample, \
63 | 'frames per sample,', MaximumExposure, 'ms maximum exposure over', \
64 | Decades, 'decades with', SamplesPerDecade, 'samples per decade.'
65 | print
66 | print 'If the exposure has not been recorded in "log scale" (you will see', \
67 | 'it in the plots), the "Decades" correspond to the "minimal exposure"', \
68 | 'and the "samples per decade" correspond to the "numbers of samples".'
69 |
70 | # Generate figure title, so we can distinguish the output
71 | Title = Sensor, Lens, FramesPerSample, 'Frames per Sample', \
72 | MaximumExposure, 'ms Maximum Exposure', Decades, 'Decades', \
73 | SamplesPerDecade, 'Samples/Decade'
74 |
75 | # Load the data from the file
76 | File = os.path.join(Root, DataFiles[whichone])
77 | Data = numpy.loadtxt(File, skiprows=3)
78 | # First line gives the full range. Read it with the snippet based on
79 | # http://stackoverflow.com/a/1904455
80 | with open(File, 'r') as f:
81 | FullRange = int(f.readline().split('=')[1])
82 |
83 | # Plot the data
84 | Labels = ['Exposure time [ms]', 'Signal', 'RMS Dyn (temporal noise)',
85 | 'Avg Dyn (temporal noise)', 'FPN (fixed pattern noise)',
86 | 'columnwise FPN', 'rowwise FPN', 'columnwise temporal noise',
87 | 'rowwise temporal noise']
88 | # The title of the plot is split over all the suplots, otherwise it destroys
89 | # the layout due to its long length
90 | plt.figure(' '.join(Title), figsize=(16, 9))
91 | # Signal
92 | ax = plt.subplot(131)
93 | plt.plot(Data[:, 0], Data[:, 1], 'o-', label=Labels[1])
94 | plt.axhline(FullRange, linestyle='--', label='Full range')
95 | plt.xlabel(Labels[0])
96 | plt.ylabel(Labels[1])
97 | plt.title(' '.join(Title[:2]))
98 | box = ax.get_position()
99 | ax.set_position([box.x0, box.y0 + box.height * 0.25,
100 | box.width, box.height * 0.75])
101 | plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.1))
102 |
103 | # We want to fit some of the data, thus make a linspace to fit over
104 | PolyfitRange = numpy.linspace(min(Data[:, 0]) - max(Data[:, 0]),
105 | 2 * max(Data[:, 0]), 200)
106 | fit = 9
107 |
108 | # Fixed pattern noise
109 | ax = plt.subplot(132)
110 | maxy = 0
111 | for i in range(4, 7):
112 | plt.plot(Data[:, 0], Data[:, i], 'o-', label=Labels[i])
113 | maxy = max(max(Data[:, i]), maxy)
114 | plt.plot(Data[:, 0], (Data[:, 1] / max(Data[:, 1])) * max(Data[:, 4]), '--',
115 | label='"Signal" scaled to max(FPN)')
116 | polynomial = numpy.poly1d(numpy.polyfit(Data[:, 0], Data[:, 4], fit))
117 | plt.plot(PolyfitRange, polynomial(PolyfitRange), '--',
118 | label='Polynomial fit (' + str(fit) + ') of FPN')
119 | plt.xlim([min(Data[:, 0]), max(Data[:, 0])])
120 | plt.ylim([0, maxy * 1.1])
121 | plt.xlabel(Labels[0])
122 | plt.ylabel('FPN')
123 | plt.title(' '.join(Title[2:6]))
124 | box = ax.get_position()
125 | ax.set_position([box.x0, box.y0 + box.height * 0.25,
126 | box.width, box.height * 0.75])
127 | plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.1))
128 |
129 | # Temporal noise
130 | ax = plt.subplot(133)
131 | maxy = 0
132 | for i in [2, 3, 7, 8]:
133 | plt.plot(Data[:, 0], Data[:, i], 'o-', label=Labels[i])
134 | maxy = max(max(Data[:, i]), maxy)
135 | plt.plot(Data[:, 0], Data[:, 1] / max(Data[:, 1]) * max(Data[:, 2]), '--',
136 | label='"Signal" scaled to max(RMS Dyn)')
137 | polynomial = numpy.poly1d(numpy.polyfit(Data[:, 0], Data[:, 2], fit))
138 | plt.plot(PolyfitRange, polynomial(PolyfitRange), '--',
139 | label='Polynomial fit (' + str(fit) + ') of RMS Dyn')
140 | plt.xlim([min(Data[:, 0]), max(Data[:, 0])])
141 | plt.ylim([0, maxy * 1.1])
142 | plt.xlabel(Labels[0])
143 | plt.ylabel('Dyn')
144 | plt.title(' '.join(Title[6:]))
145 | box = ax.get_position()
146 | ax.set_position([box.x0, box.y0 + box.height * 0.25,
147 | box.width, box.height * 0.75])
148 | plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.1))
149 | plt.savefig(os.path.join(Root, DataFiles[whichone][:-4] + '.png'),
150 | Transparent=True, bbox_inches='tight')
151 | plt.draw()
152 | plt.show()
153 |
154 | # ~ # Polynomial fit stuff
155 | # ~ plt.figure()
156 | # ~ x = Data[:, 0]
157 | # ~ y = Data[:, 9]
158 | # ~ xp = numpy.linspace(min(x)-max(x), 2*max(x), 222)
159 | # ~
160 | # ~ plt.plot(x, y, '-x', label='original')
161 | # ~ for i in range(3,10,2):
162 | # ~ polynomial = numpy.poly1d(numpy.polyfit(x, y, i))
163 | # ~ plt.plot(xp, polynomial(xp), '--', label=str(i))
164 | # ~ plt.legend(loc='best')
165 | # ~ plt.xlim([min(x), max(x)])
166 | # ~ plt.ylim([min(y), max(y)])
167 | # ~ plt.draw()
168 | # ~ plt.show()
169 |
--------------------------------------------------------------------------------
/Analysis/TarToArchive.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf8 -*-
2 |
3 | """
4 | Script to `tar` each experiment folder and send it to ftp://archivftp.psi.ch/
5 | for archival.
6 |
7 | The small bash script I wrote got much too complicated, thus I'm switching
8 | to a python script. The detection of folders and other things are based on
9 | DetectWhichImageIsRadiography.py
10 | """
11 |
12 | import os
13 | import time
14 | import subprocess
15 |
16 | import functions
17 |
18 |
19 | # Setup
20 | # If Manual selection is true, the user is asked to select one of the
21 | # experiment IDs manually, otherwise the script just goes through all the IDs
22 | # it finds in the starting folder
23 | ManualSelection = False
24 |
25 | # Where shall we start?
26 | RootFolder = '/afs/psi.ch/project/EssentialMed/MasterArbeitBFH/XrayImages'
27 | case = 3
28 | StartingFolder = []
29 | if case == 1:
30 | # Look through all folders
31 | StartingFolder = RootFolder
32 | elif case == 2:
33 | # Look for images of only one scintillator
34 | Scintillators = ('AppScinTech-HE', 'Pingseng', 'Hamamatsu', 'Toshiba')
35 | ChosenScintillator = functions.AskUser(
36 | 'Which scintillator do you want to look at?', Scintillators)
37 | StartingFolder = os.path.join(RootFolder, ChosenScintillator)
38 | elif case == 3:
39 | # Ask for what to do
40 | Scintillators = ('AppScinTech-HE', 'Pingseng', 'Hamamatsu', 'Toshiba')
41 | Sensors = ('AR0130', 'AR0132', 'MT9M001')
42 | Lenses = ('Computar-11A', 'Framos-DSL219D-650-F2.0',
43 | 'Framos-DSL224D-650-F2.0', 'Framos-DSL311A-NIR-F2.8',
44 | 'Framos-DSL949A-NIR-F2.0', 'Lensation-CHR4020',
45 | 'Lensation-CHR6020', 'Lensation-CM6014N3', 'Lensation-CY0614',
46 | 'TIS-TBL-6C-3MP', '')
47 | ChosenScintillator = functions.AskUser(
48 | 'Which scintillator do you want to look at?', Scintillators)
49 | ChosenSensor = functions.AskUser(
50 | 'Which sensor do you want to look at?', Sensors)
51 | ChosenLens = functions.AskUser(
52 | 'Which lens do you want to look at? ("empty" = "all")',
53 | Lenses)
54 | StartingFolder = os.path.join(RootFolder, ChosenScintillator,
55 | ChosenSensor, ChosenLens)
56 |
57 | # Look for all folders matching the naming convention
58 | Experiment, ExperimentID = functions.get_experiment_list(StartingFolder)
59 | print 'I found', len(Experiment), 'experiment IDs in', StartingFolder
60 |
61 | AnalyisList = []
62 | if ManualSelection:
63 | # Ask the user which experimentID to show
64 | # Concatenate the list for display purposes:
65 | # http://stackoverflow.com/a/22642307/323100
66 | Choice = functions.AskUser('Which experiment do you want to archive?',
67 | ExperimentID)
68 | AnalyisList.append(ExperimentID.index(Choice))
69 | print
70 | else:
71 | AnalyisList = range(len(Experiment))
72 |
73 | # Go through each selected experiment
74 | for Counter, SelectedExperiment in enumerate(AnalyisList):
75 | # Inform the user and start logging
76 | print 80 * '-'
77 | print str(Counter + 1) + '/' + str(len(AnalyisList)) + \
78 | ': Archiving experiment', ExperimentID[SelectedExperiment]
79 | # See if DarkDeleter.py was already run on this experiment
80 | DarkDeleterLog = os.path.join(
81 | os.path.dirname(Experiment[SelectedExperiment]),
82 | ExperimentID[SelectedExperiment] + '.deletion.log')
83 | if os.path.isfile(DarkDeleterLog):
84 | DoArchive = False
85 | # Did we really delete them? If not, we can repeat the analysis
86 | for line in open(DarkDeleterLog, 'r'):
87 | # The last line of the log file tells us if we did it or not...
88 | if 'Set "ReallyRemove"' in line:
89 | print 'We have a deletion log file, but did not actually', \
90 | 'delete the files. Proceeding...'
91 | DoArchive = True
92 | else:
93 | DoArchive = True
94 | if not DoArchive:
95 | # If we removed some files it doesn't make sense to archive again
96 | print
97 | print '\tWe already ran DarkDeleter.py on experiment', \
98 | ExperimentID[SelectedExperiment]
99 | print '\tWe thus do not archive it again.'
100 | print '\tTake a look at', os.path.join(
101 | os.path.dirname(Experiment[SelectedExperiment])[len(
102 | StartingFolder):], ExperimentID[SelectedExperiment] +
103 | '.archive.log'), 'for more info'
104 | print
105 | else:
106 | # Archive it!
107 | logfile = functions.myLogger(
108 | os.path.dirname(Experiment[SelectedExperiment]),
109 | ExperimentID[SelectedExperiment] + '.archive.log')
110 | logfile.info('Archival log file for Experiment ID %s, archived on '
111 | '%s', ExperimentID[SelectedExperiment],
112 | time.strftime('%d.%m.%Y at %H:%M:%S'))
113 | logfile.info('\nMade with "%s" at Revision %s', os.path.basename(
114 | __file__), functions.get_git_hash())
115 | logfile.info(80 * '-')
116 | # Tar the selected folder
117 | TarCommand = ['tar', '-czf', Experiment[SelectedExperiment] +
118 | '.tar.gz', '-C',
119 | os.path.dirname(Experiment[SelectedExperiment]),
120 | os.path.basename(Experiment[SelectedExperiment])]
121 | print 'Packing', ExperimentID[SelectedExperiment]
122 | logfile.info('Packing the original files with')
123 | logfile.info('---')
124 | logfile.info(' '.join(TarCommand))
125 | logfile.info('---')
126 | packit = subprocess.Popen(TarCommand, stdout=subprocess.PIPE)
127 | output, error = packit.communicate()
128 | if output:
129 | print output
130 | if error:
131 | print error
132 | time.sleep(0.5)
133 | # FTP the file to the PSI archive
134 | # We use the bookmark feature of 'lftp' to access the password. It's in
135 | # ~/.lftp/bookmarks...
136 | LFTPcommand = 'lftp -e \"mkdir -p ' + \
137 | os.path.dirname(
138 | Experiment[SelectedExperiment][len(RootFolder) + 1:]) + \
139 | ';put ' + str(Experiment[SelectedExperiment]) + '.tar.gz -o ' + \
140 | os.path.join(
141 | os.path.dirname(
142 | Experiment[SelectedExperiment][len(RootFolder) + 1:]),
143 | ExperimentID[SelectedExperiment] + '.tar.gz') + ';bye\" Ivan'
144 | print 'Transferring', \
145 | ExperimentID[SelectedExperiment] + '.tar.gz to archive'
146 | print LFTPcommand
147 | logfile.info('Transferring %s to the PSI archive with', ExperimentID[
148 | SelectedExperiment] + '.tar.gz')
149 | logfile.info('---')
150 | logfile.info(LFTPcommand)
151 | logfile.info('---')
152 | os.system(LFTPcommand)
153 | time.sleep(0.5)
154 | print 'Deleting archival file', \
155 | os.path.basename(Experiment[SelectedExperiment]) + '.tar.gz'
156 | os.remove(Experiment[SelectedExperiment] + '.tar.gz')
157 | time.sleep(0.5)
158 |
159 | print
160 | print 'Archival of', StartingFolder, 'finished'
161 |
--------------------------------------------------------------------------------
/MTF_reader_and_plotter.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Script to calculate the MTF from a real image.
5 |
6 | Based on /afs/EssentialMed/Dev/MTF.py
7 | """
8 |
9 | import matplotlib.pylab as plt
10 | import numpy as np
11 | import os
12 |
13 | plt.ion()
14 |
15 | # SETUP
16 | SelectStartPointManually = False
17 | SelectEdgeManually = False
18 | PolynomialOrder = 5
19 |
20 | # Images
21 | ImagePath = '/afs/psi.ch/project/EssentialMed/Images'
22 | ImageDir = '11-MTF'
23 |
24 | Camera = 'iPhone'
25 | # Camera = 'tiscam'
26 | # Camera = 'Elphel'
27 |
28 | if Camera == 'iPhone':
29 | # use iPhone images
30 | ImageFile = 'iPhone_with_xray_film.jpg'
31 | ImageFile = 'iPhone_with_xray_film_hdr.jpg'
32 | ImageFile = 'iPhone_with_xray_film_window.jpg'
33 | ImageFile = 'iPhone_with_xray_film_window_hdr.jpg'
34 | elif Camera == 'tiscam':
35 | # 'The imaging source' camera images from different objectives
36 | Objective = 9 # 3,6 or 9
37 | if Objective == 3:
38 | ObjectiveDir = 3.6
39 | ImageFile = 'shot0099.png' # visually the best one
40 | elif Objective == 6:
41 | ObjectiveDir = 6.0
42 | ImageFile = 'shot0364.png' # visually the best one
43 | elif Objective == 9:
44 | ObjectiveDir = 9.6
45 | ImageFile = 'shot0072.png' # visually the best one
46 | Camera = Camera + '_' + str(ObjectiveDir)
47 | elif Camera == 'Elphel':
48 | # Elphel images
49 | ImageFile = 'image.jpg'
50 | else:
51 | print 'I do not know what to do, exiting'
52 | exit()
53 |
54 |
55 | def rgb2gray(rgb):
56 | '''
57 | convert an image from rgb to grayscale
58 | http://stackoverflow.com/a/12201744/323100
59 | '''
60 | return np.dot(rgb[..., :3], [0.299, 0.587, 0.144])
61 |
62 | ImageToLoad = os.path.join(ImagePath, ImageDir, Camera, ImageFile)
63 |
64 | ImageRGB = plt.imread(ImageToLoad)
65 | Image = rgb2gray(ImageRGB)
66 |
67 |
68 | plt.imshow(np.fft.fft2(Image))
69 | # plt.imshow(Image)
70 | plt.ioff()
71 | plt.show()
72 |
73 | exit()
74 |
75 |
76 | def MTF(edgespreadfunction):
77 | '''
78 | Compute the modulation transfer function (MTF).
79 |
80 | The MTF is defined as the FFT of the line spread function.
81 | The line spread function is defined as the derivative of the edge spread
82 | function. The edge spread function are the values along an edge, ideally a
83 | knife-edge test target. See an explanation here: http://is.gd/uSC5Ve
84 | '''
85 | linespreadfunction = np.diff(edgespreadfunction)
86 | return np.abs(np.fft.fft(linespreadfunction))
87 |
88 |
89 | def LSF(edgespreadfunction):
90 | '''
91 | Compute the modulation transfer function (MTF).
92 |
93 | The MTF is defined as the FFT of the line spread function.
94 | The line spread function is defined as the derivative of the edge spread
95 | function. The edge spread function are the values along an edge, ideally a
96 | knife-edge test target. See an explanation here: http://is.gd/uSC5Ve
97 | '''
98 | return np.abs(np.diff(edgespreadfunction))
99 |
100 |
101 | def polynomialfit(data, order):
102 | '''
103 | calculate the polynomial fit of an input for a defined degree
104 | '''
105 | x, y = range(len(data)), data
106 | coefficients = np.polyfit(x, y, order)
107 | return np.polyval(coefficients, x)
108 |
109 | ImageToLoad = os.path.join(ImagePath, ImageDir, Camera, ImageFile)
110 | print 'reading', ImageToLoad
111 |
112 | # Read the image and convert it to grayscale rightaway
113 | ImageRGB = plt.imread(ImageToLoad)
114 | Image = rgb2gray(ImageRGB)
115 |
116 | ImageWidth = Image.shape[0]
117 | ImageHeight = Image.shape[1]
118 | print 'The image we loaded is', ImageWidth, 'by', ImageHeight, \
119 | 'pixels big. That is', round(ImageWidth * ImageHeight / 1e6, 3), 'MPx.'
120 |
121 | plt.subplot(221)
122 | plt.imshow(ImageRGB, origin='lower')
123 | plt.title('Pick point for drawing\n horizontal and vertical profile')
124 | if SelectStartPointManually:
125 | PickPoint = plt.ginput(1)
126 | else:
127 | if Camera == 'iPhone':
128 | PickPoint = [[1500, 1000]]
129 | elif Camera[:6] == 'tiscam':
130 | # Select middle of image...
131 | PickPoint = [[ImageHeight / 2, ImageWidth / 2]]
132 | elif Camera == 'Elphel':
133 | PickPoint = [[ImageHeight / 2, ImageWidth / 2]]
134 | plt.title('Original image')
135 | Horizon = int(PickPoint[0][1])
136 | Vertigo = int(PickPoint[0][0])
137 | if SelectStartPointManually:
138 | print 'You selected horizontal line', Horizon, 'and vertical line', Vertigo
139 | else:
140 | print 'I selected horizontal line', Horizon, 'and vertical line', Vertigo
141 | plt.hlines(Horizon, 0, ImageHeight, 'r')
142 | plt.vlines(Vertigo, 0, ImageWidth, 'b')
143 | plt.draw()
144 | plt.subplot(223)
145 | HorizontalProfile = Image[Horizon, :]
146 | plt.plot(HorizontalProfile, 'r')
147 | plt.title('Horizontal Profile')
148 | # plt.xlim(0, ImageHeight)
149 | # plt.ylim(0, 256)
150 | plt.subplot(222)
151 | VerticalProfile = Image[:, Vertigo]
152 | plt.plot(VerticalProfile, range(ImageWidth), 'b')
153 | # plt.xlim(0, 256)
154 | # plt.ylim(0, ImageWidth)
155 | plt.title('Vertical Profile')
156 | plt.draw()
157 |
158 | print 'The horizontal profile (red) goes from', min(HorizontalProfile), 'to',\
159 | max(HorizontalProfile)
160 | print 'The vertical profile (blue) goes from', min(VerticalProfile), 'to',\
161 | max(VerticalProfile)
162 |
163 | # Set range of the region we want to look at to 'Edgerange', about 10% of Image
164 | # width
165 | EdgeRange = int(round(Image.shape[0] * .05 / 10) * 10)
166 |
167 | plt.figure(figsize=(16, 9))
168 | plt.subplot(311)
169 | plt.plot(VerticalProfile)
170 | if SelectEdgeManually:
171 | plt.title('Select approximate middle of knife edge')
172 | EdgePosition = plt.ginput(1)
173 | plt.title('Vertical Profile\n(zoom reguion selected manually, width = ' +
174 | str(EdgeRange) + ' px, approx. 5% of image)')
175 | else:
176 | EdgePosition = [[LSF(VerticalProfile).argmax(), np.nan]]
177 | plt.title('Vertical Profile\n(zoom region selected automatically, width ' +
178 | '= ' + str(EdgeRange) + ' px, approx. 5% of image)')
179 | plt.axvspan(EdgePosition[0][0] - EdgeRange, EdgePosition[0][0] + EdgeRange,
180 | facecolor='r', alpha=0.5)
181 |
182 | plt.subplot(312)
183 | plt.plot(LSF(VerticalProfile))
184 | plt.axvspan(EdgePosition[0][0] - EdgeRange, EdgePosition[0][0] + EdgeRange,
185 | facecolor='r', alpha=0.5)
186 | plt.title('LSF')
187 |
188 | # plt.subplot(413)
189 | # plt.plot(MTF(VerticalProfile))
190 | # plt.title('MTF')
191 |
192 | plt.subplot(3, 3, 7)
193 | plt.plot(VerticalProfile)
194 | plt.xlim(EdgePosition[0][0] - EdgeRange, EdgePosition[0][0] + EdgeRange)
195 | plt.title('Zoomed Edge')
196 |
197 | plt.subplot(3, 3, 8)
198 | plt.plot(LSF(VerticalProfile))
199 | plt.xlim(EdgePosition[0][0] - EdgeRange, EdgePosition[0][0] + EdgeRange)
200 | plt.title('Zoomed LSF')
201 |
202 | plt.subplot(3, 3, 9)
203 | plt.plot(MTF(VerticalProfile), alpha=0.5)
204 | plt.plot(polynomialfit(MTF(VerticalProfile), PolynomialOrder), linewidth=5)
205 | plt.xlim(0, len(MTF(VerticalProfile)) / 2)
206 | plt.title('MTF with polynomial fit of order ' + str(PolynomialOrder) +
207 | '\nwith a minimum at :' +
208 | str(round(min(polynomialfit(MTF(VerticalProfile), PolynomialOrder)),
209 | 3)))
210 |
211 | plt.ioff()
212 | plt.show()
213 |
--------------------------------------------------------------------------------
/AngularOpening.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Calculate the angular opening of the lens, including the shades that we have
5 | to build in between.
6 | """
7 | from __future__ import division
8 | import optparse
9 | import sys
10 | import os
11 | import numpy
12 | import matplotlib.pyplot as plt
13 | from matplotlib.patches import Wedge
14 | from matplotlib.patches import Rectangle
15 |
16 | os.system('clear')
17 |
18 | # Use Pythons Optionparser to define and read the options, and also
19 | # give some help to the user
20 | parser = optparse.OptionParser()
21 | usage = "usage: %prog [options] arg"
22 | parser.add_option('-d', dest='Distance', type='float', default=134,
23 | metavar='123', help='Scintillator-CMOS distance [mm]. '
24 | 'Default = %default mm')
25 | parser.add_option('-f', dest='FOV', type='float', default=450 / 3,
26 | metavar='430', help='Desired field of view [mm]. Default = '
27 | '%default mm')
28 | parser.add_option('-o', dest='Overlap', type='float', default=2,
29 | metavar='16', help='Overlap between the images [%]. Default '
30 | '= %default %')
31 | parser.add_option('-p', dest='ParaventLength', type='float', default=100,
32 | metavar='123', help='Length of the paravents. Default = '
33 | '%default mm')
34 | parser.add_option('-l', dest='LensLength', type='float', default=16.8,
35 | metavar='11.3', help='Length of the lens. Default = '
36 | '%default mm')
37 | parser.add_option('-b', dest='BackFocalLength', type='float', default=6.5,
38 | metavar='9.0', help='Back focal length of the lens. Default '
39 | '= %default mm')
40 | parser.add_option('-s', dest='SaveImage', default=True, action='store_true',
41 | help='Write output, (Default: %default)')
42 | (options, args) = parser.parse_args()
43 |
44 | # TBL 6 C 3MP specifications, as from TIS and copied here: http://cl.ly/YQ4Z
45 | # FOV = 145 mm without overlap
46 | # LensLengtht = 10 mm
47 | # BackFocalLength = 6.5 mm
48 | # Measured FOV at a distance of 13 cm is 135 x 105 mm
49 |
50 | # show the help if the needed parameters (distance and FOV) are not given
51 | if options.Distance is None or options.FOV is None:
52 | parser.print_help()
53 | print ''
54 | print 'Example:'
55 | print 'The command below shows the configuration of a detector with '
56 | print 'an optics with an opening angle of 78° used to get a field'
57 | print 'of view of 50 cm:'
58 | print ''
59 | print sys.argv[0], '-a 78 -f 50'
60 | print ''
61 | sys.exit(1)
62 | print ''
63 |
64 | # tan(\alpha/2) = (FOV/2) / Distance
65 | # Distance = (FOV/2)/tan(\alpha/2)
66 |
67 | print 'We calculate with a CMOS-Scintillator distance of', options.Distance, \
68 | 'mm.'
69 | print 'With a back focal length of', options.BackFocalLength, \
70 | 'mm and a lens length of', options.LensLength, 'mm we have a distance of',\
71 | options.Distance - options.BackFocalLength - options.LensLength, \
72 | 'mm from the front of the lens to the scintillator.'
73 |
74 | print 'The FOV is corrected with an overlap of', options.Overlap, '% from', \
75 | options.FOV, 'mm to',
76 | options.FOV = options.FOV * (1 + (options.Overlap / 100))
77 | print options.FOV, 'mm.'
78 |
79 | print 'For a visible FOV of', options.FOV, 'mm at a distance of', \
80 | options.Distance, 'mm we get a calculated opening angle of the lens of',
81 | OpeningAngle = numpy.rad2deg(numpy.arctan((options.FOV / 2) /
82 | options.Distance)) * 2
83 | print round(OpeningAngle, 1), 'degrees'
84 |
85 | plt.figure(figsize=(5, 15))
86 | for Displacement in (0, - options.FOV / (1 + options.Overlap / 100),
87 | options.FOV / (1 + options.Overlap / 100)):
88 | # Central axis
89 | plt.axhline(Displacement, color='k', linestyle='--')
90 |
91 | # CMOS
92 | cmoscolor = 'b'
93 | plt.plot((0, 0), (Displacement + 3, Displacement - 3), linewidth='5',
94 | color=cmoscolor)
95 |
96 | # Lens
97 | rect = Rectangle((options.BackFocalLength, Displacement - 14 / 2),
98 | options.LensLength, 14, facecolor="#aaaaaa")
99 | plt.gca().add_patch(rect)
100 |
101 | # Opening angle, based on CMOS
102 | wedge = Wedge((0, Displacement), options.Distance * 0.309,
103 | -OpeningAngle / 2, OpeningAngle / 2, fill=True, color='r',
104 | alpha=0.125)
105 | plt.gca().add_patch(wedge)
106 | plt.plot((0, options.Distance),
107 | (Displacement, Displacement + options.FOV / 2), color='k',
108 | linestyle='--', alpha=0.25)
109 | plt.plot((0, options.Distance),
110 | (Displacement, Displacement - options.FOV / 2), color='k',
111 | linestyle='--', alpha=0.25)
112 |
113 | # Scintillator FOV
114 | screencolor = 'k'
115 | plt.plot([options.Distance, options.Distance],
116 | [Displacement + (options.FOV / 2),
117 | Displacement - (options.FOV / 2)], linewidth='6',
118 | color=screencolor)
119 | screencolor = 'g'
120 | plt.plot([options.Distance, options.Distance],
121 | [Displacement + (options.FOV / 2),
122 | Displacement - (options.FOV / 2)], linewidth='4',
123 | color=screencolor)
124 |
125 | # FOV drawn from center of lens
126 | beamcolor = 'r'
127 | plt.plot([options.BackFocalLength + options.LensLength,
128 | options.Distance], [Displacement, Displacement + options.FOV /
129 | 2], beamcolor)
130 | plt.plot([options.BackFocalLength + options.LensLength,
131 | options.Distance], [Displacement, Displacement - options.FOV /
132 | 2], beamcolor)
133 |
134 | # Paravents. Position calculated back from overlap
135 | paraventcolor = 'k'
136 | plt.plot([0, options.ParaventLength],
137 | [Displacement - (options.FOV / (1 + options.Overlap / 100) / 2),
138 | Displacement - (options.FOV / (1 + options.Overlap / 100) / 2)],
139 | linewidth='5', color=paraventcolor)
140 |
141 | # Paravent blocking,
142 | beamcolor = 'g'
143 | plt.plot([options.BackFocalLength + options.LensLength, options.Distance],
144 | [Displacement, Displacement + options.FOV / 2], beamcolor)
145 | plt.plot([options.BackFocalLength + options.LensLength, options.Distance],
146 | [Displacement, Displacement - options.FOV / 2], beamcolor)
147 |
148 | # Nice plotting
149 |
150 | plt.title('Angular opening: ' + str(round(OpeningAngle, 2)) + '\nFOV size: ' +
151 | str(options.FOV) + ' mm (including overlap of ' +
152 | str(options.Overlap) + ' %)\nWorking Distance: ' +
153 | str('%.2f' % options.Distance) + ' mm\nParavent length: ' +
154 | str('%.2f' % options.ParaventLength) + ' mm')
155 | plt.xlabel('Distance [mm]')
156 | plt.axis('equal')
157 |
158 | if options.SaveImage:
159 | SaveName = 'Paravents_' + str(str('%.2f' % OpeningAngle)) + '_wd_' + \
160 | str('%.2f' % options.Distance) + 'mm_FOV_' + \
161 | str('%.2f' % options.FOV) + 'mm'
162 | FigureName = ''.join([SaveName, '.png'])
163 | plt.savefig(FigureName)
164 | print 'Figure saved to ' + FigureName
165 |
166 | plt.show()
167 |
--------------------------------------------------------------------------------