petal-qc 0.0.15__py3-none-any.whl → 0.0.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- petal_qc/BTreport/CheckBTtests.py +92 -33
- petal_qc/BTreport/bustapeReport.py +2 -1
- petal_qc/__init__.py +7 -2
- petal_qc/{test/getPetalCoreTestSummary.py → getPetalCoreTestSummary.py} +36 -14
- petal_qc/metrology/PetalMetrology.py +0 -13
- petal_qc/metrology/compare_Cores.py +44 -0
- petal_qc/metrology/coreMetrology.py +0 -12
- petal_qc/metrology/do_Metrology.py +0 -13
- petal_qc/metrology/petal_flatness.py +0 -13
- petal_qc/metrology/readAVSdata.py +2 -0
- petal_qc/metrology/uploadPetalInformation.py +20 -20
- petal_qc/test/analyzeMetrologyTable.py +119 -0
- petal_qc/test/checkAVStests.py +2 -2
- petal_qc/test/checkCoreShipments.py +57 -0
- petal_qc/test/createMetrologyTable.py +87 -0
- petal_qc/test/getAVStests.py +21 -3
- petal_qc/test/prepareDESYfiles.py +2 -1
- petal_qc/test/test_Graphana.py +2 -1
- petal_qc/thermal/IRPetalParam.py +2 -0
- petal_qc/thermal/PipeFit.py +48 -19
- petal_qc/thermal/analyze_IRCore.py +34 -24
- petal_qc/thermal/contours.py +18 -12
- petal_qc/thermal/create_IRCore.py +40 -9
- petal_qc/thermal/create_core_report.py +8 -5
- petal_qc/thermal/show_IR_petal.py +1 -10
- petal_qc/utils/ArgParserUtils.py +41 -0
- petal_qc/utils/docx_utils.py +18 -6
- {petal_qc-0.0.15.dist-info → petal_qc-0.0.17.dist-info}/METADATA +2 -2
- {petal_qc-0.0.15.dist-info → petal_qc-0.0.17.dist-info}/RECORD +32 -28
- {petal_qc-0.0.15.dist-info → petal_qc-0.0.17.dist-info}/WHEEL +1 -1
- {petal_qc-0.0.15.dist-info → petal_qc-0.0.17.dist-info}/entry_points.txt +1 -0
- {petal_qc-0.0.15.dist-info → petal_qc-0.0.17.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Analyze the table generated by createMetrologyTable."""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import math
|
|
7
|
+
import argparse
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import numpy as np
|
|
10
|
+
from numpy.linalg import norm
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
from lmfit.models import LinearModel
|
|
13
|
+
|
|
14
|
+
from petal_qc.utils.fit_utils import draw_best_fit
|
|
15
|
+
|
|
16
|
+
def distance(P1, P2, P3):
|
|
17
|
+
C = np.cross(P2-P1, P3-P1)
|
|
18
|
+
D = C/norm(P2-P1)
|
|
19
|
+
return D
|
|
20
|
+
|
|
21
|
+
def remove_outliers_indx(data, cut=2.0, debug=False):
|
|
22
|
+
"""Remove points far away form the rest.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
----
|
|
26
|
+
data : The data
|
|
27
|
+
cut: max allowed distance
|
|
28
|
+
debug: be verbose if True.
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
index of valid pints in data array.
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
d = np.abs(data - np.median(data))
|
|
36
|
+
mdev = np.median(d)
|
|
37
|
+
s = d / (mdev if mdev else 1.)
|
|
38
|
+
indx = np.where(s < cut)[0]
|
|
39
|
+
return indx
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main(options):
|
|
43
|
+
"""main entry."""
|
|
44
|
+
fig_width = 12.0
|
|
45
|
+
fig_height = 1.2*fig_width/3.0
|
|
46
|
+
|
|
47
|
+
T = pd.read_csv(options.files[0])
|
|
48
|
+
|
|
49
|
+
if options.mould > 0:
|
|
50
|
+
x = 1000*T.loc[T['mould'] == options.mould, 'fd_dx'].values
|
|
51
|
+
y = 1000*T.loc[T['mould'] == options.mould, 'fd_dy'].values
|
|
52
|
+
|
|
53
|
+
else:
|
|
54
|
+
x = 1000*T['fd_dx'].values
|
|
55
|
+
y = 1000*T['fd_dy'].values
|
|
56
|
+
|
|
57
|
+
fig, ax = plt.subplots(nrows=1, ncols=3, tight_layout=True, figsize=(fig_width, fig_height))
|
|
58
|
+
fig.suptitle("Relative Position FD01-FD02")
|
|
59
|
+
ax[0].set_title("FD01-FD02")
|
|
60
|
+
ax[0].set_aspect('equal', adjustable='box')
|
|
61
|
+
ax[0].set_xlim(-150, 150)
|
|
62
|
+
ax[0].set_ylim(-150, 150)
|
|
63
|
+
circle = plt.Circle((0,0), 75, color="red", alpha=0.25)
|
|
64
|
+
ax[0].add_patch(circle)
|
|
65
|
+
circle = plt.Circle((0,0), 25, color="green", alpha=0.25)
|
|
66
|
+
ax[0].add_patch(circle)
|
|
67
|
+
|
|
68
|
+
ax[0].set_xlabel("X (µm)")
|
|
69
|
+
ax[0].set_ylabel("Y (µm)")
|
|
70
|
+
ax[0].grid()
|
|
71
|
+
|
|
72
|
+
ax[0].scatter(x, y, marker='.')
|
|
73
|
+
|
|
74
|
+
model = LinearModel()
|
|
75
|
+
params = model.guess(y, x=x)
|
|
76
|
+
result = model.fit(y, params, x=x)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
P1 = np.array([0, result.eval(x=0)])
|
|
80
|
+
P2 = np.array([1, result.eval(x=1)])
|
|
81
|
+
values = []
|
|
82
|
+
for v, w in zip(x, y):
|
|
83
|
+
P3 = np.array([v, w])
|
|
84
|
+
values.append(distance(P1, P2, P3))
|
|
85
|
+
|
|
86
|
+
indx = remove_outliers_indx(values)
|
|
87
|
+
|
|
88
|
+
xx = x[indx]
|
|
89
|
+
yy = y[indx]
|
|
90
|
+
params = model.guess(yy, xx)
|
|
91
|
+
result = model.fit(yy, params, x=xx)
|
|
92
|
+
result.plot_fit(ax=ax[0])
|
|
93
|
+
print("slope {:.5f}, intercept {:.5f}".format(result.best_values['slope'], result.best_values["intercept"]))
|
|
94
|
+
angle = 180*math.atan( result.best_values['slope'])/math.pi
|
|
95
|
+
print("angle {:.5f} deg.".format(angle))
|
|
96
|
+
|
|
97
|
+
ax[1].set_xlim(-150, 150)
|
|
98
|
+
ax[1].set_xlabel("X (µm)")
|
|
99
|
+
ax[1].grid()
|
|
100
|
+
ax[1].hist(x)
|
|
101
|
+
|
|
102
|
+
ax[2].set_xlim(-150, 150)
|
|
103
|
+
ax[2].set_xlabel("Y (µm)")
|
|
104
|
+
ax[2].grid()
|
|
105
|
+
ax[2].hist(y)
|
|
106
|
+
|
|
107
|
+
plt.show()
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
parser = argparse.ArgumentParser()
|
|
111
|
+
parser.add_argument('files', nargs='*', help="Input files")
|
|
112
|
+
parser.add_argument('--mould', default=-1, type=int, help="mould index")
|
|
113
|
+
|
|
114
|
+
opts = parser.parse_args()
|
|
115
|
+
if len(opts.files) == 0:
|
|
116
|
+
print("I need at least one input file")
|
|
117
|
+
sys.exit()
|
|
118
|
+
|
|
119
|
+
main(opts)
|
petal_qc/test/checkAVStests.py
CHANGED
|
@@ -169,8 +169,8 @@ if __name__ == "__main__":
|
|
|
169
169
|
client = dlg.get_client()
|
|
170
170
|
|
|
171
171
|
try:
|
|
172
|
-
|
|
173
|
-
the_test = analyze_avs_metrology(client, "20USEBC1000124", "PPC.015")
|
|
172
|
+
main(client)
|
|
173
|
+
# the_test = analyze_avs_metrology(client, "20USEBC1000124", "PPC.015")
|
|
174
174
|
#rc = ITkDButils.upload_test(client, the_test, check_runNumber=True)
|
|
175
175
|
#if rc:
|
|
176
176
|
# print(rc)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""List shipments."""
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from itkdb_gtk import ITkDBlogin, ITkDButils
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main(session):
|
|
10
|
+
"""List shipments from AVS to CERN and IFIC containing CORES."""
|
|
11
|
+
|
|
12
|
+
payload = {
|
|
13
|
+
"filterMap": {
|
|
14
|
+
"sender": "AVS",
|
|
15
|
+
"recipient": ["CERN", "IFIC"],
|
|
16
|
+
"status": "delivered"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
shpmts = session.get("listShipmentsByInstitution", json=payload)
|
|
20
|
+
for s in shpmts:
|
|
21
|
+
items = session.get("listShipmentItems", json={"shipment": s["id"]})
|
|
22
|
+
cores = []
|
|
23
|
+
for it in items:
|
|
24
|
+
if it["component"]["componentType"]['code'] == "CORE_PETAL":
|
|
25
|
+
if "PPC." in it["component"]["alternativeIdentifier"]:
|
|
26
|
+
cores.append(it["component"]["alternativeIdentifier"])
|
|
27
|
+
|
|
28
|
+
if len(cores) == 0:
|
|
29
|
+
continue
|
|
30
|
+
|
|
31
|
+
print("Shipment {}".format(s["name"]))
|
|
32
|
+
print("AVS -> {}".format(s["recipient"]["code"]))
|
|
33
|
+
print(":> {}".format(s["sentTs"]))
|
|
34
|
+
for c in cores:
|
|
35
|
+
print("\t{}".format(c))
|
|
36
|
+
|
|
37
|
+
print("\n")
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
# ITk_PB authentication
|
|
41
|
+
dlg = ITkDBlogin.ITkDBlogin()
|
|
42
|
+
client = dlg.get_client()
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
main(client)
|
|
46
|
+
# the_test = analyze_avs_metrology(client, "20USEBC1000124", "PPC.015")
|
|
47
|
+
#rc = ITkDButils.upload_test(client, the_test, check_runNumber=True)
|
|
48
|
+
#if rc:
|
|
49
|
+
# print(rc)
|
|
50
|
+
|
|
51
|
+
except Exception as E:
|
|
52
|
+
print(E)
|
|
53
|
+
|
|
54
|
+
dlg.die()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Create table for metrology analysis."""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import re
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from argparse import ArgumentParser
|
|
9
|
+
from petal_qc.utils.all_files import all_files
|
|
10
|
+
from petal_qc.utils.ArgParserUtils import RangeListAction
|
|
11
|
+
|
|
12
|
+
r_petal_id = re.compile("PPC.([0-9]*)")
|
|
13
|
+
|
|
14
|
+
db_file = Path("/Users/lacasta/cernbox/workspace/Petal-QC/PetalMould.csv")
|
|
15
|
+
|
|
16
|
+
petal_db = {}
|
|
17
|
+
with open(db_file, "r", encoding="utf-8") as fin:
|
|
18
|
+
for i, line in enumerate(fin):
|
|
19
|
+
if i==0:
|
|
20
|
+
continue
|
|
21
|
+
|
|
22
|
+
values = [x.strip() for x in line.split(",")]
|
|
23
|
+
petal_db[values[0]] = values[1:]
|
|
24
|
+
|
|
25
|
+
def main(options):
|
|
26
|
+
"""Main entry."""
|
|
27
|
+
ifolder = Path(options.input)
|
|
28
|
+
if not ifolder.exists():
|
|
29
|
+
print("Input folder does not exist.\n{}".format(ifolder))
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
has_list = len(options.cores) != 0
|
|
33
|
+
|
|
34
|
+
ofile = open(options.out, "w", encoding="utf-8")
|
|
35
|
+
ofile.write("PetalID,SN,side,mould,fd_dx,fd_dy,paralelism,R0,R1,R2,R3S0,R3S1,R4S0,R4S1,R5S0,R5S1,flatness\n")
|
|
36
|
+
|
|
37
|
+
for fnam in all_files(ifolder.as_posix(), "*.json"):
|
|
38
|
+
fstem = fnam.stem
|
|
39
|
+
if "PPC." not in fstem:
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
R = r_petal_id.search(fstem)
|
|
43
|
+
if R is None:
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
petal_id = R.group(0)
|
|
47
|
+
pid = int(R.group(1))
|
|
48
|
+
|
|
49
|
+
if has_list and pid not in options.cores:
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
SN = petal_db[petal_id][0]
|
|
53
|
+
side = 1 if "back" in fstem else 0
|
|
54
|
+
mould = int(petal_db[petal_id][1])
|
|
55
|
+
|
|
56
|
+
data = None
|
|
57
|
+
with open(fnam, "r", encoding="utf-8") as fin:
|
|
58
|
+
data = json.load(fin)
|
|
59
|
+
|
|
60
|
+
ofile.write("{}, {}, {}, {}".format(petal_id, SN, side, mould))
|
|
61
|
+
print("data SN", data["component"])
|
|
62
|
+
D = data["results"]["METROLOGY"]["REL_POS_DELTA"]["FD01-FD02"]
|
|
63
|
+
ofile.write(",{:.5f}, {:.5f}".format(D[0], D[1]))
|
|
64
|
+
|
|
65
|
+
D = data["results"]["METROLOGY"]["PARALLELISM"]
|
|
66
|
+
ofile.write(",{:.5f}".format(D))
|
|
67
|
+
|
|
68
|
+
for x in data["results"]["METROLOGY"]["FLATNESS_LOCAL"]:
|
|
69
|
+
ofile.write(", {:.5f}".format(x))
|
|
70
|
+
|
|
71
|
+
D = data["results"]["METROLOGY"]["FLATNESS_GLOBAL"]
|
|
72
|
+
ofile.write(", {:.5f}".format(D))
|
|
73
|
+
|
|
74
|
+
ofile.write("\n")
|
|
75
|
+
|
|
76
|
+
ofile.close()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__":
|
|
80
|
+
parser = ArgumentParser()
|
|
81
|
+
parser.add_argument("--input-folder", dest="input", default=".", help="Input folder")
|
|
82
|
+
parser.add_argument("--out", default="out.csv", help="Output table.")
|
|
83
|
+
parser.add_argument("--cores", dest="cores", action=RangeListAction, default=[],
|
|
84
|
+
help="Create list of cores to analyze. The list is made with numbers or ranges (ch1:ch2 or ch1:ch2:step) ")
|
|
85
|
+
|
|
86
|
+
opts = parser.parse_args()
|
|
87
|
+
main(opts)
|
petal_qc/test/getAVStests.py
CHANGED
|
@@ -4,6 +4,7 @@ import sys
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
import numpy as np
|
|
6
6
|
import matplotlib.pyplot as plt
|
|
7
|
+
from argparse import ArgumentParser
|
|
7
8
|
|
|
8
9
|
try:
|
|
9
10
|
import petal_qc
|
|
@@ -14,7 +15,7 @@ except ImportError:
|
|
|
14
15
|
|
|
15
16
|
from itkdb_gtk import ITkDBlogin
|
|
16
17
|
import petal_qc.utils.docx_utils as docx_utils
|
|
17
|
-
|
|
18
|
+
from petal_qc.utils.ArgParserUtils import RangeListAction
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
def get_value(results, code):
|
|
@@ -171,7 +172,7 @@ def plot_weighing(weights, tick_labels, document, show_total=False):
|
|
|
171
172
|
ax.legend(loc="upper left", ncol=4)
|
|
172
173
|
|
|
173
174
|
|
|
174
|
-
def main(session):
|
|
175
|
+
def main(session, options):
|
|
175
176
|
"""Entry point"""
|
|
176
177
|
# find all cores
|
|
177
178
|
# Now all the objects
|
|
@@ -200,11 +201,16 @@ def main(session):
|
|
|
200
201
|
for T in core_tests:
|
|
201
202
|
counter[T]=0
|
|
202
203
|
|
|
204
|
+
mould_db = {}
|
|
203
205
|
for core in core_list:
|
|
204
206
|
SN = core["serialNumber"]
|
|
205
207
|
altid = core['alternativeIdentifier']
|
|
206
208
|
if "PPC" not in altid:
|
|
207
209
|
continue
|
|
210
|
+
|
|
211
|
+
pID = int(altid[4:])
|
|
212
|
+
if len(options.cores)>0 and pID not in options.cores:
|
|
213
|
+
continue
|
|
208
214
|
|
|
209
215
|
petal_ids.append(altid)
|
|
210
216
|
location = core["currentLocation"]['code']
|
|
@@ -243,6 +249,7 @@ def main(session):
|
|
|
243
249
|
mould_desc = do_manufacturing(good_tests["MANUFACTURING"])
|
|
244
250
|
pos = mould_desc.rfind('.')
|
|
245
251
|
mould_id = mould_desc[pos+1:]
|
|
252
|
+
mould_db[altid] = (SN, mould_id)
|
|
246
253
|
if mould_id not in Mould_values:
|
|
247
254
|
Mould_values[mould_id] = {}
|
|
248
255
|
|
|
@@ -302,13 +309,24 @@ def main(session):
|
|
|
302
309
|
plot_weighing(weights, petal_ids, document)
|
|
303
310
|
plot_metrology(M_values, Mould_values, petal_ids, document)
|
|
304
311
|
document.save("AVStests.docx")
|
|
312
|
+
|
|
313
|
+
with open("mould_db.csv", "w", encoding="utf-8") as fout:
|
|
314
|
+
fout.write("PetalID, SerialNo, MouldID\n")
|
|
315
|
+
for key, val in mould_db.items():
|
|
316
|
+
fout.write("{}, {}, {}\n".format(key, val[0], val[1]))
|
|
317
|
+
|
|
305
318
|
plt.show()
|
|
306
319
|
|
|
307
320
|
if __name__ == "__main__":
|
|
308
321
|
# ITk_PB authentication
|
|
322
|
+
parser = ArgumentParser()
|
|
323
|
+
parser.add_argument("--cores", dest="cores", action=RangeListAction, default=[],
|
|
324
|
+
help="Create list of cores to analyze. The list is made with numbers or ranges (ch1:ch2 or ch1:ch2:step) ")
|
|
325
|
+
|
|
326
|
+
|
|
309
327
|
dlg = ITkDBlogin.ITkDBlogin()
|
|
310
328
|
session = dlg.get_client()
|
|
311
329
|
|
|
312
|
-
main(session)
|
|
330
|
+
main(session, options)
|
|
313
331
|
|
|
314
332
|
dlg.die()
|
|
@@ -96,7 +96,8 @@ def main(folder, out_folder):
|
|
|
96
96
|
data = ""
|
|
97
97
|
for data_type, fnam in values.items():
|
|
98
98
|
if fnam is None:
|
|
99
|
-
print("
|
|
99
|
+
print("Missing file name for {} {} [{}]".format(petal_id, side, data_type))
|
|
100
|
+
continue
|
|
100
101
|
|
|
101
102
|
with open(fnam, "r", encoding="UTF-8") as ifile:
|
|
102
103
|
data += ifile.read()
|
petal_qc/test/test_Graphana.py
CHANGED
|
@@ -19,7 +19,8 @@ from petal_qc.thermal import IRBFile
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
options = IRPetalParam()
|
|
22
|
-
options.
|
|
22
|
+
options.institute = "IFIC"
|
|
23
|
+
options.files = [Path("~/tmp/thermal/IFIC-thermal/IRB_files/PPC.008.irb").expanduser().resolve()]
|
|
23
24
|
getter = IRDataGetter.factory(options.institute, options)
|
|
24
25
|
DB = ReadGraphana("localhost")
|
|
25
26
|
irbf = IRBFile.open_file(options.files)
|
petal_qc/thermal/IRPetalParam.py
CHANGED
|
@@ -27,6 +27,7 @@ class IRPetalParam(object):
|
|
|
27
27
|
self.rotate = True # Rotate to have a vertical petal in mirror image
|
|
28
28
|
self.debug = False # To debug
|
|
29
29
|
self.report = False #
|
|
30
|
+
self.graphana = None # Graphana server
|
|
30
31
|
|
|
31
32
|
if values is not None:
|
|
32
33
|
self.set_values(values)
|
|
@@ -61,6 +62,7 @@ class IRPetalParam(object):
|
|
|
61
62
|
help="Either IFIC or DESY to treat the different files")
|
|
62
63
|
parser.add_argument("--thrs", type=float, default=P.thrs, help="Temperature threshold")
|
|
63
64
|
parser.add_argument("--tco2", type=float, default=P.tco2, help="CO2 Inlet temperature")
|
|
65
|
+
parser.add_argument("--graphana", type=str, default=None, help="Graphana server.")
|
|
64
66
|
parser.add_argument("--gauss_size", type=int, default=P.gauss_size, help="Radius of gausian filtering")
|
|
65
67
|
parser.add_argument("--distance", type=float, default=P.distance, help="Distance in contour beteween slices")
|
|
66
68
|
parser.add_argument("--npoints", type=int, default=P.npoints, help="Number of points per segment")
|
petal_qc/thermal/PipeFit.py
CHANGED
|
@@ -8,7 +8,6 @@ from pathlib import Path
|
|
|
8
8
|
import matplotlib.path as mplPath
|
|
9
9
|
import matplotlib.pyplot as plt
|
|
10
10
|
import numpy as np
|
|
11
|
-
import numpy.linalg as linalg
|
|
12
11
|
from scipy.optimize import least_squares
|
|
13
12
|
|
|
14
13
|
from petal_qc.thermal import contours
|
|
@@ -43,6 +42,9 @@ class PipeFit(object):
|
|
|
43
42
|
self.bounds = [[-math.pi, 0.8, 0.8, -np.inf, -np.inf],
|
|
44
43
|
[math.pi, 3.0, 3.0, np.inf, np.inf]]
|
|
45
44
|
|
|
45
|
+
self.core_center = None
|
|
46
|
+
self.core_band = None
|
|
47
|
+
|
|
46
48
|
def set_front(self, is_front=True):
|
|
47
49
|
"""Sets for a front image.
|
|
48
50
|
|
|
@@ -119,6 +121,7 @@ class PipeFit(object):
|
|
|
119
121
|
"""
|
|
120
122
|
DB = np.load(pipe_file)
|
|
121
123
|
sensors = []
|
|
124
|
+
pipe = []
|
|
122
125
|
for key, val in DB.items():
|
|
123
126
|
if key == "pipe":
|
|
124
127
|
pipe = val
|
|
@@ -213,7 +216,7 @@ class PipeFit(object):
|
|
|
213
216
|
print("Center {:5.3f}, {:5.3f}".format(M[5], M[6]))
|
|
214
217
|
|
|
215
218
|
@staticmethod
|
|
216
|
-
def get_data_center(data, fraction=0.5, y0=None):
|
|
219
|
+
def get_data_center(data, fraction=0.5, y0=None, min_pts=5):
|
|
217
220
|
"""Compute the data center.
|
|
218
221
|
|
|
219
222
|
Assumes it is in the half height, and the X is the average of the points
|
|
@@ -221,19 +224,19 @@ class PipeFit(object):
|
|
|
221
224
|
"""
|
|
222
225
|
if y0 is None:
|
|
223
226
|
bounding_box = contours.contour_bounds(data)
|
|
224
|
-
y0 =
|
|
227
|
+
y0 = bounding_box[1] + fraction*(bounding_box[3]-bounding_box[1])
|
|
225
228
|
|
|
226
229
|
window = 10
|
|
227
230
|
while True:
|
|
228
231
|
stripe = data[np.abs(data[:, 1] - y0) < window]
|
|
229
|
-
if len(stripe) >
|
|
232
|
+
if len(stripe) > min_pts:
|
|
230
233
|
break
|
|
231
234
|
|
|
232
235
|
window += 1
|
|
233
236
|
|
|
234
237
|
m0 = np.mean(stripe, axis=0)
|
|
235
238
|
center = np.array([m0[0], y0])
|
|
236
|
-
return center
|
|
239
|
+
return center, (np.min(stripe[:, 0]), np.max(stripe[:, 0]))
|
|
237
240
|
|
|
238
241
|
@staticmethod
|
|
239
242
|
def guess_pipe_type(data, fraction=0.25) -> int:
|
|
@@ -256,7 +259,7 @@ class PipeFit(object):
|
|
|
256
259
|
|
|
257
260
|
D = data[:, 0:2] - P0
|
|
258
261
|
m0 = np.mean(D, axis=0)
|
|
259
|
-
center = PipeFit.get_data_center(D, y0=m0[1])
|
|
262
|
+
center, _ = PipeFit.get_data_center(D, y0=m0[1])
|
|
260
263
|
# fig, ax = plt.subplots(1,1)
|
|
261
264
|
# ax.plot(D[:,0], D[:, 1], 'o', label="Data")
|
|
262
265
|
# ax.plot(center[0], center[1], '*', label="Center")
|
|
@@ -273,9 +276,16 @@ class PipeFit(object):
|
|
|
273
276
|
@staticmethod
|
|
274
277
|
def guess_pipe_angle(data, center):
|
|
275
278
|
"""Get an estimation of th epipe angle."""
|
|
276
|
-
c1 = PipeFit.get_data_center(data, 0.
|
|
277
|
-
delta = c1 - center
|
|
278
|
-
angle = math.atan(delta[
|
|
279
|
+
c1, _ = PipeFit.get_data_center(data, 0.)
|
|
280
|
+
delta = np.abs(c1 - center)
|
|
281
|
+
angle = math.atan(delta[1]/delta[0])
|
|
282
|
+
while angle<0:
|
|
283
|
+
angle += 2.0*math.pi
|
|
284
|
+
|
|
285
|
+
if angle > 1.5*math.pi:
|
|
286
|
+
angle -= math.pi/2.0
|
|
287
|
+
|
|
288
|
+
angle = 0.5*math.pi - angle
|
|
279
289
|
return angle
|
|
280
290
|
|
|
281
291
|
@staticmethod
|
|
@@ -289,8 +299,11 @@ class PipeFit(object):
|
|
|
289
299
|
|
|
290
300
|
def initial_guess(self, data):
|
|
291
301
|
"""Make a first guess of the transform."""
|
|
292
|
-
Mdata = self.get_data_center(data)
|
|
302
|
+
Mdata, _ = self.get_data_center(data)
|
|
293
303
|
theta = self.guess_pipe_angle(data, Mdata)
|
|
304
|
+
if self.front:
|
|
305
|
+
theta = -theta
|
|
306
|
+
|
|
294
307
|
T = -Mdata
|
|
295
308
|
dxd = np.amax(data, axis=0) - np.amin(data, axis=0)
|
|
296
309
|
dxp = np.amax(self.cpipe, axis=0) - np.amin(self.cpipe, axis=0)
|
|
@@ -313,7 +326,11 @@ class PipeFit(object):
|
|
|
313
326
|
def get_residuals(self, M):
|
|
314
327
|
"""Compute intersecting area."""
|
|
315
328
|
out = self.transform_data(self.data, M)
|
|
316
|
-
|
|
329
|
+
y_max = np.max(out[:,1])
|
|
330
|
+
y_min = np.min(out[:,1])
|
|
331
|
+
|
|
332
|
+
height = y_max - y_min
|
|
333
|
+
use_area = False
|
|
317
334
|
if use_area:
|
|
318
335
|
path = mplPath.Path(out)
|
|
319
336
|
ngood = 0.0
|
|
@@ -327,20 +344,29 @@ class PipeFit(object):
|
|
|
327
344
|
real_area = 100*ngood/ntot
|
|
328
345
|
|
|
329
346
|
else:
|
|
330
|
-
area =
|
|
331
|
-
real_area =
|
|
347
|
+
area = 1.0
|
|
348
|
+
real_area = 1
|
|
332
349
|
|
|
333
350
|
npts = len(self.data)
|
|
334
351
|
D = np.zeros([npts, 2])
|
|
335
352
|
ddd = np.zeros(npts)
|
|
353
|
+
sum_weights = 0.0
|
|
354
|
+
weights = np.zeros(npts)
|
|
336
355
|
for i in range(npts):
|
|
337
356
|
X = out[i, :]
|
|
338
357
|
dst, P = contours.find_closest_point(X[0], X[1], self.cpipe)
|
|
339
358
|
D[i, :] = P - X
|
|
340
359
|
ddd[i] = dst
|
|
360
|
+
W = 1 + 1.6*(y_max - X[1])/height
|
|
361
|
+
weights[i] = W
|
|
362
|
+
sum_weights += W
|
|
341
363
|
|
|
342
364
|
# return value
|
|
343
|
-
|
|
365
|
+
if use_area:
|
|
366
|
+
res = D.flatten()*area
|
|
367
|
+
|
|
368
|
+
else:
|
|
369
|
+
res = np.dot(ddd, weights)/sum_weights
|
|
344
370
|
|
|
345
371
|
if self.debug:
|
|
346
372
|
dbg_ax. clear()
|
|
@@ -420,14 +446,17 @@ class PipeFit(object):
|
|
|
420
446
|
elif val > self.bounds[1][i]:
|
|
421
447
|
val = 0.99 * self.bounds[0][i]
|
|
422
448
|
M[i] = val
|
|
423
|
-
|
|
449
|
+
|
|
424
450
|
if self.debug:
|
|
425
451
|
print("\n** Initial guess")
|
|
426
452
|
self.print_transform(M)
|
|
427
453
|
else:
|
|
428
454
|
M = M0
|
|
429
455
|
|
|
430
|
-
self.
|
|
456
|
+
self.core_center, self.core_band = self.get_data_center(data)
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
self.data = contours.contour_simplify(data, 1.25*factor)
|
|
431
460
|
# self.data = data
|
|
432
461
|
verbose = 0
|
|
433
462
|
if self.debug:
|
|
@@ -436,7 +465,7 @@ class PipeFit(object):
|
|
|
436
465
|
res = least_squares(self.get_residuals, M,
|
|
437
466
|
method='trf',
|
|
438
467
|
# ftol=5e-16,
|
|
439
|
-
#
|
|
468
|
+
#xtol=1.0e-10,
|
|
440
469
|
# max_nfev=10000,
|
|
441
470
|
# diff_step=[0.05, 0.02, 0.02, 50, 50],
|
|
442
471
|
bounds=self.bounds,
|
|
@@ -551,7 +580,7 @@ def main(data_file, opts):
|
|
|
551
580
|
ax[0].plot(PF.center[0], PF.center[1], 'o')
|
|
552
581
|
ax[0].set_title("Pipe")
|
|
553
582
|
|
|
554
|
-
center = PF.get_data_center(data)
|
|
583
|
+
center, _ = PF.get_data_center(data)
|
|
555
584
|
ax[1].plot(data[:, 0], data[:, 1])
|
|
556
585
|
ax[1].plot(center[0], center[1], 'o')
|
|
557
586
|
ax[1].set_title("Data on IR")
|
|
@@ -560,7 +589,7 @@ def main(data_file, opts):
|
|
|
560
589
|
plt.figure(fign)
|
|
561
590
|
out = PF.transform_data(PF.data, R)
|
|
562
591
|
aout = PF.transform_data(data, R)
|
|
563
|
-
center = PF.get_data_center(aout)
|
|
592
|
+
center, _ = PF.get_data_center(aout)
|
|
564
593
|
ax[2].plot(PF.pipe[:, 0], PF.pipe[:, 1])
|
|
565
594
|
ax[2].plot(out[:, 0], out[:, 1], 'o')
|
|
566
595
|
ax[2].plot(center[0], center[1], 'o')
|