Skip to content

Commit 74226f4

Browse files
authored
Merge pull request #190 from fmi-faim/migrate-eicm
Migrate https://github.com/fmi-faim/eicm to faim-ipa.
2 parents 5f2ddbf + 066fd46 commit 74226f4

16 files changed

+896
-0
lines changed

resources/eicm/MeasurementDetail.mrf

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<bts:MeasurementDetail bts:Version="1.0" bts:OperatorName="reitsabi" bts:Title="20221221-Field-illumination-QC" bts:Application="" bts:BeginTime="2022-12-21T14:39:35.8551584+01:00" bts:EndTime="2022-12-21T14:55:54.7397183+01:00" bts:MeasurementSettingFileName="QC_dyes_60x__Cellvis_glass.mes" bts:ColumnCount="12" bts:RowCount="8" bts:TimePointCount="4" bts:FieldCount="16" bts:ZCount="36" bts:TargetSystem="CV7000 FM03" bts:ReleaseNumber="R2.02.07" bts:Status="0" xmlns:bts="http://www.yokogawa.co.jp/BTS/BTSSchema/1.0">
3+
<bts:MeasurementSamplePlate bts:Name="Dyes_60xW_Cellvis" bts:WellPlateFileName="Dyes_60xW_Cellvis.wpi" bts:WellPlateProductFileName="1009680002_Cellvis_#P96-1.5H-N.wpp" />
4+
<bts:MeasurementChannel bts:Ch="1" bts:HorizontalPixelDimension="0.10833333333333334" bts:VerticalPixelDimension="0.10833333333333334" bts:CameraNumber="2" bts:InputBitDepth="16" bts:InputLevel="5000" bts:HorizontalPixels="2560" bts:VerticalPixels="2160" bts:FilterWheelPosition="5" bts:FilterPosition="1" bts:ShadingCorrectionSource="SC_BP445-45_60xW_M01_CH01.tif" bts:ObjectiveMagnificationRatio="1" bts:OriginalHorizontalPixels="2560" bts:OriginalVerticalPixels="2160" />
5+
<bts:MeasurementChannel bts:Ch="2" bts:HorizontalPixelDimension="0.10833333333333334" bts:VerticalPixelDimension="0.10833333333333334" bts:CameraNumber="2" bts:InputBitDepth="16" bts:InputLevel="10000" bts:HorizontalPixels="2560" bts:VerticalPixels="2160" bts:FilterWheelPosition="5" bts:FilterPosition="2" bts:ShadingCorrectionSource="SC_BP525-50_60xW_M01_CH02.tif" bts:ObjectiveMagnificationRatio="1" bts:OriginalHorizontalPixels="2560" bts:OriginalVerticalPixels="2160" />
6+
<bts:MeasurementChannel bts:Ch="3" bts:HorizontalPixelDimension="0.10833333333333334" bts:VerticalPixelDimension="0.10833333333333334" bts:CameraNumber="1" bts:InputBitDepth="16" bts:InputLevel="10000" bts:HorizontalPixels="2560" bts:VerticalPixels="2160" bts:FilterWheelPosition="4" bts:FilterPosition="1" bts:ShadingCorrectionSource="SC_BP600-37_60xW_M01_CH03.tif" bts:ObjectiveMagnificationRatio="1" bts:OriginalHorizontalPixels="2560" bts:OriginalVerticalPixels="2160" />
7+
<bts:MeasurementChannel bts:Ch="4" bts:HorizontalPixelDimension="0.10833333333333334" bts:VerticalPixelDimension="0.10833333333333334" bts:CameraNumber="1" bts:InputBitDepth="16" bts:InputLevel="10000" bts:HorizontalPixels="2560" bts:VerticalPixels="2160" bts:FilterWheelPosition="4" bts:FilterPosition="2" bts:ShadingCorrectionSource="SC_BP676-29_60xW_M01_CH04.tif" bts:ObjectiveMagnificationRatio="1" bts:OriginalHorizontalPixels="2560" bts:OriginalVerticalPixels="2160" />
8+
<bts:MeasurementChannel bts:Ch="5" bts:HorizontalPixelDimension="0.10833333333333334" bts:VerticalPixelDimension="0.10833333333333334" bts:CameraNumber="2" bts:InputBitDepth="16" bts:InputLevel="10000" bts:HorizontalPixels="2560" bts:VerticalPixels="2160" bts:FilterWheelPosition="5" bts:FilterPosition="2" bts:ShadingCorrectionSource="" bts:ObjectiveMagnificationRatio="1" bts:OriginalHorizontalPixels="2560" bts:OriginalVerticalPixels="2160" />
9+
<bts:MeasurementChannel bts:Ch="6" bts:HorizontalPixelDimension="0.10833333333333334" bts:VerticalPixelDimension="0.10833333333333334" bts:CameraNumber="2" bts:InputBitDepth="16" bts:InputLevel="65535" bts:HorizontalPixels="2560" bts:VerticalPixels="2160" bts:FilterWheelPosition="5" bts:FilterPosition="2" bts:ShadingCorrectionSource="SC_BP525-50_60xW_M01_CH06.tif" bts:ObjectiveMagnificationRatio="1" bts:OriginalHorizontalPixels="2560" bts:OriginalVerticalPixels="2160" />
10+
</bts:MeasurementDetail>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<bts:MeasurementSetting bts:Version="1.0" bts:ProductID="1009680002" bts:Application="" bts:Columns="12" bts:Rows="8" xmlns:bts="http://www.yokogawa.co.jp/BTS/BTSSchema/1.0">
3+
<bts:Timelapse>
4+
<bts:Timeline bts:Name="Time Line 1" bts:InitialTime="0" bts:Period="496" bts:Interval="496" bts:ExpectedTime="496" bts:Color="#ff44ee66" bts:OverrideExpectedTime="false">
5+
<bts:WellSequence bts:IsSelected="true">
6+
<bts:TargetWell bts:Column="10" bts:Row="6">true</bts:TargetWell>
7+
</bts:WellSequence>
8+
<bts:PointSequence bts:Method="FixedPosition">
9+
<bts:FixedPosition bts:IsProportional="false">
10+
<bts:Point bts:X="-1350" bts:Y="1350" />
11+
<bts:Point bts:X="-450" bts:Y="1350" />
12+
<bts:Point bts:X="450" bts:Y="1350" />
13+
<bts:Point bts:X="1350" bts:Y="1350" />
14+
<bts:Point bts:X="1350" bts:Y="450" />
15+
<bts:Point bts:X="450" bts:Y="450" />
16+
<bts:Point bts:X="-450" bts:Y="450" />
17+
<bts:Point bts:X="-1350" bts:Y="450" />
18+
<bts:Point bts:X="-1350" bts:Y="-450" />
19+
<bts:Point bts:X="-450" bts:Y="-450" />
20+
<bts:Point bts:X="450" bts:Y="-450" />
21+
<bts:Point bts:X="1350" bts:Y="-450" />
22+
<bts:Point bts:X="1350" bts:Y="-1350" />
23+
<bts:Point bts:X="450" bts:Y="-1350" />
24+
<bts:Point bts:X="-450" bts:Y="-1350" />
25+
<bts:Point bts:X="-1350" bts:Y="-1350" />
26+
</bts:FixedPosition>
27+
</bts:PointSequence>
28+
<bts:ActionList bts:RunMode="sequential" bts:AFSearch="bottom">
29+
<bts:ActionAcquire3D bts:XOffset="0" bts:YOffset="0" bts:AFShiftBase="-2" bts:TopDistance="20" bts:BottomDistance="-15" bts:SliceLength="1" bts:UseSoftFocus="false">
30+
<bts:Ch>1</bts:Ch>
31+
</bts:ActionAcquire3D>
32+
</bts:ActionList>
33+
</bts:Timeline>
34+
<bts:Timeline bts:Name="Time Line 2" bts:InitialTime="865" bts:Period="290" bts:Interval="0" bts:ExpectedTime="190" bts:Color="#ff44ee66" bts:OverrideExpectedTime="false">
35+
<bts:WellSequence bts:IsSelected="true">
36+
<bts:TargetWell bts:Column="10" bts:Row="3">true</bts:TargetWell>
37+
</bts:WellSequence>
38+
<bts:PointSequence bts:Method="FixedPosition">
39+
<bts:FixedPosition bts:IsProportional="false">
40+
<bts:Point bts:X="-1350" bts:Y="1350" />
41+
<bts:Point bts:X="-450" bts:Y="1350" />
42+
<bts:Point bts:X="450" bts:Y="1350" />
43+
<bts:Point bts:X="1350" bts:Y="1350" />
44+
<bts:Point bts:X="1350" bts:Y="450" />
45+
<bts:Point bts:X="450" bts:Y="450" />
46+
<bts:Point bts:X="-450" bts:Y="450" />
47+
<bts:Point bts:X="-1350" bts:Y="450" />
48+
<bts:Point bts:X="-1350" bts:Y="-450" />
49+
<bts:Point bts:X="-450" bts:Y="-450" />
50+
<bts:Point bts:X="450" bts:Y="-450" />
51+
<bts:Point bts:X="1350" bts:Y="-450" />
52+
<bts:Point bts:X="1350" bts:Y="-1350" />
53+
<bts:Point bts:X="450" bts:Y="-1350" />
54+
<bts:Point bts:X="-450" bts:Y="-1350" />
55+
<bts:Point bts:X="-1350" bts:Y="-1350" />
56+
</bts:FixedPosition>
57+
</bts:PointSequence>
58+
<bts:ActionList bts:RunMode="sequential" bts:AFSearch="bottom">
59+
<bts:ActionAcquire3D bts:XOffset="0" bts:YOffset="0" bts:AFShiftBase="-2" bts:TopDistance="20" bts:BottomDistance="-15" bts:SliceLength="1" bts:UseSoftFocus="false">
60+
<bts:Ch>2</bts:Ch>
61+
</bts:ActionAcquire3D>
62+
</bts:ActionList>
63+
</bts:Timeline>
64+
<bts:Timeline bts:Name="Time Line 3" bts:InitialTime="496" bts:Period="329" bts:Interval="0" bts:ExpectedTime="179" bts:Color="#ff44ee66" bts:OverrideExpectedTime="false">
65+
<bts:WellSequence bts:IsSelected="true">
66+
<bts:TargetWell bts:Column="10" bts:Row="4">true</bts:TargetWell>
67+
</bts:WellSequence>
68+
<bts:PointSequence bts:Method="FixedPosition">
69+
<bts:FixedPosition bts:IsProportional="false">
70+
<bts:Point bts:X="-1350" bts:Y="1350" />
71+
<bts:Point bts:X="-450" bts:Y="1350" />
72+
<bts:Point bts:X="450" bts:Y="1350" />
73+
<bts:Point bts:X="1350" bts:Y="1350" />
74+
<bts:Point bts:X="1350" bts:Y="450" />
75+
<bts:Point bts:X="450" bts:Y="450" />
76+
<bts:Point bts:X="-450" bts:Y="450" />
77+
<bts:Point bts:X="-1350" bts:Y="450" />
78+
<bts:Point bts:X="-1350" bts:Y="-450" />
79+
<bts:Point bts:X="-450" bts:Y="-450" />
80+
<bts:Point bts:X="450" bts:Y="-450" />
81+
<bts:Point bts:X="1350" bts:Y="-450" />
82+
<bts:Point bts:X="1350" bts:Y="-1350" />
83+
<bts:Point bts:X="450" bts:Y="-1350" />
84+
<bts:Point bts:X="-450" bts:Y="-1350" />
85+
<bts:Point bts:X="-1350" bts:Y="-1350" />
86+
</bts:FixedPosition>
87+
</bts:PointSequence>
88+
<bts:ActionList bts:RunMode="sequential" bts:AFSearch="bottom">
89+
<bts:ActionAcquire3D bts:XOffset="0" bts:YOffset="0" bts:AFShiftBase="-2" bts:TopDistance="20" bts:BottomDistance="-15" bts:SliceLength="1" bts:UseSoftFocus="false">
90+
<bts:Ch>3</bts:Ch>
91+
</bts:ActionAcquire3D>
92+
</bts:ActionList>
93+
</bts:Timeline>
94+
<bts:Timeline bts:Name="Time Line 4" bts:InitialTime="675" bts:Period="329" bts:Interval="0" bts:ExpectedTime="190" bts:Color="#ff44ee66" bts:OverrideExpectedTime="false">
95+
<bts:WellSequence bts:IsSelected="true">
96+
<bts:TargetWell bts:Column="10" bts:Row="5">true</bts:TargetWell>
97+
</bts:WellSequence>
98+
<bts:PointSequence bts:Method="FixedPosition">
99+
<bts:FixedPosition bts:IsProportional="false">
100+
<bts:Point bts:X="-1350" bts:Y="1350" />
101+
<bts:Point bts:X="-450" bts:Y="1350" />
102+
<bts:Point bts:X="450" bts:Y="1350" />
103+
<bts:Point bts:X="1350" bts:Y="1350" />
104+
<bts:Point bts:X="1350" bts:Y="450" />
105+
<bts:Point bts:X="450" bts:Y="450" />
106+
<bts:Point bts:X="-450" bts:Y="450" />
107+
<bts:Point bts:X="-1350" bts:Y="450" />
108+
<bts:Point bts:X="-1350" bts:Y="-450" />
109+
<bts:Point bts:X="-450" bts:Y="-450" />
110+
<bts:Point bts:X="450" bts:Y="-450" />
111+
<bts:Point bts:X="1350" bts:Y="-450" />
112+
<bts:Point bts:X="1350" bts:Y="-1350" />
113+
<bts:Point bts:X="450" bts:Y="-1350" />
114+
<bts:Point bts:X="-450" bts:Y="-1350" />
115+
<bts:Point bts:X="-1350" bts:Y="-1350" />
116+
</bts:FixedPosition>
117+
</bts:PointSequence>
118+
<bts:ActionList bts:RunMode="sequential" bts:AFSearch="bottom">
119+
<bts:ActionAcquire3D bts:XOffset="0" bts:YOffset="0" bts:AFShiftBase="-2" bts:TopDistance="20" bts:BottomDistance="-15" bts:SliceLength="1" bts:UseSoftFocus="false">
120+
<bts:Ch>4</bts:Ch>
121+
</bts:ActionAcquire3D>
122+
</bts:ActionList>
123+
</bts:Timeline>
124+
</bts:Timelapse>
125+
<bts:LightSourceList>
126+
<bts:LightSource bts:Name="405nm" bts:Type="Laser" bts:WaveLength="405" bts:Power="100" />
127+
<bts:LightSource bts:Name="488nm" bts:Type="Laser" bts:WaveLength="488" bts:Power="100" />
128+
<bts:LightSource bts:Name="561nm" bts:Type="Laser" bts:WaveLength="561" bts:Power="100" />
129+
<bts:LightSource bts:Name="640nm" bts:Type="Laser" bts:WaveLength="640" bts:Power="100" />
130+
<bts:LightSource bts:Name="Lamp" bts:Type="Lamp" bts:WaveLength="0" bts:Power="30" />
131+
</bts:LightSourceList>
132+
<bts:ChannelList>
133+
<bts:Channel bts:Ch="1" bts:Target="405" bts:ObjectiveID="60004" bts:Objective="60x W" bts:Magnification="60" bts:MethodID="1" bts:Method="Confocal Fluorescence 405/488/561/640 nm" bts:FilterID="1" bts:Acquisition="BP445/45" bts:ExposureTime="600" bts:Binning="1" bts:Color="#FF1B54FF" bts:MinLevel="0" bts:MaxLevel="0.16008524507960367" bts:Kind="ConfocalFluorescence" bts:AndorParameterID="32" bts:AndorParameter="Gain 1,4" bts:CameraType="Andor" bts:InputLevel="5000" bts:Fluorophore="">
134+
<bts:LightSourceName>405nm</bts:LightSourceName>
135+
</bts:Channel>
136+
<bts:Channel bts:Ch="2" bts:Target="488" bts:ObjectiveID="60004" bts:Objective="60x W" bts:Magnification="60" bts:MethodID="1" bts:Method="Confocal Fluorescence 405/488/561/640 nm" bts:FilterID="2" bts:Acquisition="BP525/50" bts:ExposureTime="70" bts:Binning="1" bts:Color="#FF00FFA1" bts:MinLevel="0" bts:MaxLevel="0.79942334210856225" bts:Kind="ConfocalFluorescence" bts:AndorParameterID="32" bts:AndorParameter="Gain 1,4" bts:CameraType="Andor" bts:InputLevel="10000" bts:Fluorophore="">
137+
<bts:LightSourceName>488nm</bts:LightSourceName>
138+
</bts:Channel>
139+
<bts:Channel bts:Ch="3" bts:Target="561" bts:ObjectiveID="60004" bts:Objective="60x W" bts:Magnification="60" bts:MethodID="1" bts:Method="Confocal Fluorescence 405/488/561/640 nm" bts:FilterID="5" bts:Acquisition="BP600/37" bts:ExposureTime="50" bts:Binning="1" bts:Color="#FFFF8200" bts:MinLevel="0" bts:MaxLevel="0.29171367682085975" bts:Kind="ConfocalFluorescence" bts:AndorParameterID="32" bts:AndorParameter="Gain 1,4" bts:CameraType="Andor" bts:InputLevel="10000" bts:Fluorophore="">
140+
<bts:LightSourceName>561nm</bts:LightSourceName>
141+
</bts:Channel>
142+
<bts:Channel bts:Ch="4" bts:Target="640" bts:ObjectiveID="60004" bts:Objective="60x W" bts:Magnification="60" bts:MethodID="1" bts:Method="Confocal Fluorescence 405/488/561/640 nm" bts:FilterID="6" bts:Acquisition="BP676/29" bts:ExposureTime="70" bts:Binning="1" bts:Color="#FFFF1B00" bts:MinLevel="0" bts:MaxLevel="0.17888930675692605" bts:Kind="ConfocalFluorescence" bts:AndorParameterID="32" bts:AndorParameter="Gain 1,4" bts:CameraType="Andor" bts:InputLevel="10000" bts:Fluorophore="">
143+
<bts:LightSourceName>640nm</bts:LightSourceName>
144+
</bts:Channel>
145+
<bts:Channel bts:Ch="5" bts:Target="Brightfield" bts:ObjectiveID="60004" bts:Objective="60x W" bts:Magnification="60" bts:MethodID="4" bts:Method="Brightfield(confocal path)" bts:FilterID="2" bts:Acquisition="BP525/50" bts:ExposureTime="100" bts:Binning="1" bts:Color="#FFFFFFFF" bts:MinLevel="0" bts:MaxLevel="0.09862142099681849" bts:Kind="Brightfield" bts:AndorParameterID="32" bts:AndorParameter="Gain 1,4" bts:CameraType="Andor" bts:InputLevel="10000" bts:Fluorophore="">
146+
<bts:LightSourceName>Lamp</bts:LightSourceName>
147+
</bts:Channel>
148+
<bts:Channel bts:Ch="6" bts:Target="FITC" bts:ObjectiveID="60004" bts:Objective="60x W" bts:Magnification="60" bts:MethodID="1" bts:Method="Confocal Fluorescence 405/488/561/640 nm" bts:FilterID="2" bts:Acquisition="BP525/50" bts:ExposureTime="15" bts:Binning="1" bts:Color="#FF00FFA1" bts:MinLevel="0" bts:MaxLevel="0.79942334210856225" bts:Kind="ConfocalFluorescence" bts:AndorParameterID="32" bts:AndorParameter="Gain 1,4" bts:CameraType="Andor" bts:InputLevel="65535" bts:Fluorophore="">
149+
<bts:LightSourceName>488nm</bts:LightSourceName>
150+
</bts:Channel>
151+
</bts:ChannelList>
152+
</bts:MeasurementSetting>
1.65 KB
Binary file not shown.
1.59 KB
Binary file not shown.

src/faim_ipa/eicm/__init__.py

Whitespace-only changes.

src/faim_ipa/eicm/estimator/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import numpy as np
2+
from scipy.optimize import curve_fit
3+
from skimage.measure import centroid
4+
5+
6+
def ellipsoid_2D(coords, A, B, mu_x, mu_y, cxx, cxy, cyy):
7+
"""
8+
Evaluates the given ellipsoid parameters at the provided coordinates.
9+
10+
:param coords: at which the evaluation takes place
11+
:param A: amplitude
12+
:param B: offset
13+
:param mu_x: x location of the centroid
14+
:param mu_y: y location of the centroid
15+
:param cxx: covariance matrix term at 00
16+
:param cxy: covariance matrix term at 01
17+
:param cyy: covariance matrix term at 11
18+
:return:
19+
"""
20+
inv = np.linalg.inv(np.array([[cxx, cxy], [cxy, cyy]]) + np.identity(2) * 1e-8)
21+
22+
return (
23+
A
24+
* np.exp(
25+
-0.5
26+
* (
27+
inv[0, 0] * (coords[:, 1] - mu_x) ** 2
28+
+ 2 * inv[0, 1] * (coords[:, 1] - mu_x) * (coords[:, 0] - mu_y)
29+
+ inv[1, 1] * (coords[:, 0] - mu_y) ** 2
30+
)
31+
)
32+
+ B
33+
)
34+
35+
36+
def get_cov_matrix(img):
37+
"""
38+
Compute covariance matrix of the provided image.
39+
40+
:param img:
41+
:return: covariance matrix
42+
"""
43+
44+
def cov(x, y, i):
45+
return np.sum(x * y * i) / np.sum(i)
46+
47+
y, x = np.meshgrid(
48+
np.arange(img.shape[0]),
49+
np.arange(img.shape[1]),
50+
indexing="ij",
51+
)
52+
cen = centroid(img)
53+
y = y.ravel() - cen[0]
54+
x = x.ravel() - cen[1]
55+
56+
cxx = cov(x, x, img.ravel())
57+
cyy = cov(y, y, img.ravel())
58+
cxy = cov(x, y, img.ravel())
59+
60+
return np.array([[cxx, cxy], [cxy, cyy]])
61+
62+
63+
def get_estimates(data):
64+
"""
65+
Estimate parameters of 2D Gaussian fit to the data.
66+
:param data:
67+
:return: (amplitude, offset, centroid-x, centroid-y, cxx, cxy, cyy)
68+
"""
69+
max_ = data.max()
70+
mean_ = data.mean()
71+
cy, cx = centroid(data)
72+
cov = get_cov_matrix(data)
73+
return [
74+
max_ - mean_,
75+
mean_,
76+
cx,
77+
cy,
78+
cov[0, 0],
79+
cov[0, 1],
80+
cov[1, 1],
81+
]
82+
83+
84+
def get_coords(data):
85+
"""
86+
Compute coordinate grid of provided 2D data.
87+
:param data:
88+
:return: coordinates
89+
"""
90+
yy = np.arange(data.shape[0])
91+
xx = np.arange(data.shape[1])
92+
y, x = np.meshgrid(yy, xx, indexing="ij")
93+
return np.stack([y.ravel(), x.ravel()], -1)
94+
95+
96+
def fit_gaussian_2d(data, coords):
97+
"""
98+
Fit 2D ellipsoid to data.
99+
:param data: intensities
100+
:param coords: pixel coordinates
101+
:return: popt, pcov (see scipy.optimize.curve_fit)
102+
"""
103+
return curve_fit(
104+
ellipsoid_2D,
105+
coords,
106+
data.ravel(),
107+
p0=get_estimates(data),
108+
)
109+
110+
111+
def compute_fitted_matrix(coords, ellipsoid_parameters, shape):
112+
"""
113+
Compute 2D ellipsoid for provided set of parameters
114+
:param coords: pixel coordinates
115+
:param ellipsoid_parameters:
116+
:param shape: of the final output
117+
:return: matrix
118+
"""
119+
return ellipsoid_2D(coords, *ellipsoid_parameters).reshape(shape)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import numpy as np
2+
from scipy.ndimage import gaussian_filter
3+
4+
5+
def create_blurred_illumination_matrix(img, sigma: float = 20):
6+
"""
7+
Takes an image and blurs it with a Gaussian.
8+
9+
:param img: The acquired illumination field.
10+
:param sigma: Gaussian sigma.
11+
:return: illumination matrix
12+
"""
13+
matrix = gaussian_filter(input=img.astype(np.float32), sigma=sigma, mode="nearest")
14+
return matrix
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import numpy as np
2+
3+
4+
def polynomial_fit(mip, polynomial_degree=3, order=None):
5+
"""
6+
Fits a polynomial f(x, y) to the provided mip.
7+
8+
:param mip: to fit
9+
:param polynomial_degree: highest degree of individual coefficients
10+
:param order: polynomial order
11+
:return: fitted matrix, string describing the polynomial
12+
"""
13+
Y, X = np.meshgrid(
14+
np.linspace(0, 1, mip.shape[0]), np.linspace(0, 1, mip.shape[1]), indexing="ij"
15+
)
16+
17+
coeffs = np.ones((polynomial_degree + 1, polynomial_degree + 1))
18+
19+
A = np.zeros((coeffs.size, X.size))
20+
21+
poly_str = ["1"]
22+
for row_idx, (i, j) in enumerate(np.ndindex(coeffs.shape)):
23+
if order is not None and i + j > order:
24+
tmp = np.zeros_like(X)
25+
else:
26+
tmp = coeffs[i, j] * X**j * Y**i
27+
28+
substr = []
29+
if j > 0:
30+
substr.append(f"X^{j}")
31+
if i > 0:
32+
substr.append(f"Y^{i}")
33+
34+
if len(substr) > 0:
35+
poly_str.append(" * ".join(substr))
36+
A[row_idx] = tmp.ravel()
37+
38+
poly_str = " + ".join(poly_str)
39+
40+
x, residuals, rank, s = np.linalg.lstsq(A.T, np.ravel(mip), rcond=None)
41+
42+
x = x.reshape((polynomial_degree + 1, polynomial_degree + 1))
43+
fit = np.polynomial.polynomial.polyval2d(Y, X, x).reshape(mip.shape)
44+
return fit, poly_str

src/faim_ipa/eicm/estimator/utils.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import numpy as np
2+
3+
4+
def normalize_matrix(matrix):
5+
"""
6+
Normalize matrix to [0, 1] by dividing it by its maximum value.
7+
8+
:param matrix:
9+
:return: normalized matrix
10+
"""
11+
return np.clip(matrix / matrix.max(), 0.0, 1.0)

src/faim_ipa/eicm/preprocessing/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)