Skip to content

Commit 9667a44

Browse files
committed
Merge branch '20-bpm-add-min-max-for-manual-scale-mode' into 'master'
Resolve "bpm add min-max for manual scale mode" Closes esrf-bliss#20 See merge request limagroup/Lima-tango-python!42
2 parents b956d26 + 5f4bc2b commit 9667a44

File tree

1 file changed

+115
-45
lines changed

1 file changed

+115
-45
lines changed

plugins/Bpm.py

Lines changed: 115 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,21 @@ class BpmDeviceServer(BasePostProcess):
7373
#--------- Add you global variables here --------------------------
7474
BPM_TASK_NAME = "BpmTask"
7575
BVDATA_TASK_NAME = "BVDataTask"
76+
77+
ImageType2Bpp = {
78+
Core.Bpp8 : 8 ,
79+
Core.Bpp10 : 10 ,
80+
Core.Bpp12 : 12 ,
81+
Core.Bpp14 : 14 ,
82+
Core.Bpp16 : 16,
83+
Core.Bpp32 : 32 ,
84+
Core.Bpp8S : 7 ,
85+
Core.Bpp10S : 9 ,
86+
Core.Bpp12S : 11 ,
87+
Core.Bpp14S : 13 ,
88+
Core.Bpp16S : 15,
89+
Core.Bpp32S : 31 ,
90+
}
7691
#------------------------------------------------------------------
7792
# Device constructor
7893
#------------------------------------------------------------------
@@ -86,6 +101,9 @@ def __init__(self,cl, name):
86101

87102
self.palette = self.init_palette()
88103

104+
# initialize min max for image scaling
105+
#self.min_max = [0, 2**(self.ImageType2Bpp[_control_ref().image().getImageType()])]
106+
89107
BasePostProcess.__init__(self,cl,name)
90108
self.init_device()
91109

@@ -189,10 +207,10 @@ def getResults(self, from_index=0) :
189207
for i,r in enumerate(results):
190208
result_array[i][0] = r.timestamp
191209
result_array[i][1] = self.validate_number(r.beam_intensity)
192-
result_array[i][2] = self.validate_number(r.beam_center_x, max_value=max_width)
193-
result_array[i][3] = self.validate_number(r.beam_center_y, max_value=max_height)
194-
result_array[i][4] = self.validate_number(r.beam_fwhm_x, fallback_value=0)
195-
result_array[i][5] = self.validate_number(r.beam_fwhm_y, fallback_value=0)
210+
result_array[i][2] = self.validate_number(r.beam_center_x, max_value=max_width) * self.calibration[0]
211+
result_array[i][3] = self.validate_number(r.beam_center_y, max_value=max_height) * self.calibration[1]
212+
result_array[i][4] = self.validate_number(r.beam_fwhm_x, fallback_value=0) * self.calibration[0]
213+
result_array[i][5] = self.validate_number(r.beam_fwhm_y, fallback_value=0) * self.calibration[1]
196214
result_array[i][6] = r.frameNumber
197215
return result_array.ravel()
198216

@@ -205,7 +223,6 @@ def GetPixelIntensity(self, coordinate):
205223
except:
206224
return -1
207225

208-
##############BACKGROUND : work through bpm device commande.
209226
def TakeBackground(self):
210227
ctControl = _control_ref()
211228
extOpt = ctControl.externalOperation()
@@ -344,8 +361,11 @@ def write_lut_method(self,attr):
344361
prop = {'lut_method': data}
345362
PyTango.Database().put_device_property(self.get_name(), prop)
346363

347-
else:
348-
print ("wrong lut method, 'LINEAR' or 'LOG'")
364+
else:
365+
PyTango.Except.throw_exception('WrongData',\
366+
f'Wrong value lut_method: {data}, use log or linear instead',
367+
'LimaCCD Class')
368+
349369

350370
def is_lut_method_allowed(self,mode) :
351371
return True
@@ -413,6 +433,48 @@ def read_bvdata(self,attr):
413433

414434
attr.set_value(self.bvdata_format,self.bvdata)
415435

436+
def read_min_max(self,attr):
437+
attr.set_value(self.min_max)
438+
439+
def write_min_max(self, attr):
440+
data = attr.get_write_value()
441+
max = 2**(self.ImageType2Bpp[_control_ref().image().getImageType()])
442+
range = [0, max]
443+
#reset asked to default
444+
if data[0] == 0 and data[1] == 0:
445+
data[1] = max
446+
447+
if data[0] > data[1]:
448+
PyTango.Except.throw_exception('WrongData',\
449+
f'Wrong values min_max: {data}, max < min',
450+
'LimaCCD Class')
451+
452+
if data[0] > max or data[1] > max:
453+
PyTango.Except.throw_exception('WrongData',\
454+
f'Wrong value min_max: {data}, out of range {range}',
455+
'LimaCCD Class'
456+
)
457+
self.min_max[0] = data[0]
458+
self.min_max[1] = data[1]
459+
#update the property
460+
prop = {'min_max': data}
461+
PyTango.Database().put_device_property(self.get_name(), prop)
462+
463+
def is_min_max_allowed(self,mode) :
464+
return True
465+
466+
def read_return_bpm_profiles(self,attr):
467+
attr.set_value(self.return_bpm_profiles)
468+
469+
def write_return_bpm_profiles(self,attr):
470+
data = attr.get_write_value()
471+
self.return_bpm_profiles = data
472+
#update the property
473+
prop = {'return_bpmpylon/PylonBase.h_profiles': data}
474+
PyTango.Database().put_device_property(self.get_name(), prop)
475+
476+
def is_return_bpm_profiles_allowed(self,mode) :
477+
return True
416478

417479
#==================================================================
418480
#
@@ -455,7 +517,15 @@ class BpmDeviceServerClass(PyTango.DeviceClass):
455517
'jpeg_quality':
456518
[PyTango.DevLong,
457519
"Set jpeg encoding quality from 1-100",
458-
80]
520+
80],
521+
'return_bpm_profiles':
522+
[PyTango.DevBoolean,
523+
"return bpm profiles if True, otherwise the beammark profiles",
524+
True],
525+
"min_max":
526+
[PyTango.DevVarLongArray,
527+
"min/max value for manual scaling",
528+
[0,65535] ],
459529
}
460530

461531

@@ -505,7 +575,9 @@ class BpmDeviceServerClass(PyTango.DeviceClass):
505575
'bvdata':[[PyTango.DevEncoded, PyTango.SCALAR, PyTango.READ]],
506576
'calibration': [[PyTango.DevDouble, PyTango.SPECTRUM, PyTango.READ_WRITE, 2 ]],
507577
'beammark': [[PyTango.DevLong, PyTango.SPECTRUM, PyTango.READ_WRITE, 2 ]],
508-
'jpeg_quality': [[PyTango.DevLong, PyTango.SCALAR, PyTango.READ_WRITE]]
578+
'jpeg_quality': [[PyTango.DevLong, PyTango.SCALAR, PyTango.READ_WRITE]],
579+
'min_max': [[PyTango.DevULong64, PyTango.SPECTRUM, PyTango.READ_WRITE, 2 ]],
580+
'return_bpm_profiles': [[PyTango.DevBoolean, PyTango.SCALAR, PyTango.READ_WRITE]],
509581
}
510582

511583

@@ -581,26 +653,23 @@ def construct_bvdata(bpm):
581653
jpegFile = StringIO()
582654
image_type = _control_ref().image().getImageType()
583655

584-
if image_type==0: #Bpp8
585-
bpp = 8
586-
elif image_type==2: #Bpp10
587-
bpp = 10
588-
elif image_type==4: #Bpp12
589-
bpp = 12
590-
elif image_type==6: #Bpp14
591-
bpp = 14
592-
elif image_type==8: #Bpp16
593-
bpp = 16
594-
elif image_type==16: #Bpp24
595-
bpp = 24
596-
elif image_type==10: #Bpp32
597-
bpp = 32
598-
599-
min_val = image.buffer.min()
600-
max_val = image.buffer.max()
601-
602-
scale_image = image.buffer
656+
bpp = bpm.ImageType2Bpp[image_type]
657+
658+
# manual scaling: use the user image min/max intensity to filter
659+
if not bpm.autoscale:
660+
min_val = bpm.min_max[0]
661+
max_val = bpm.min_max[1]
662+
if max_val == 0: max_val = 1
663+
scale_image = image.buffer.clip(min_val, max_val)
603664

665+
# auto scaling: use natural image intensity
666+
else:
667+
min_val = image.buffer.min()
668+
max_val = image.buffer.max()
669+
if max_val == 0: max_val = 1
670+
scale_image = image.buffer
671+
672+
# logarithmic scaling
604673
if bpm.lut_method=="LOG":
605674
if min_val>0:
606675
scale_image = numpy.log10(scale_image)
@@ -610,19 +679,12 @@ def construct_bvdata(bpm):
610679
min_val = numpy.log10(min_val)
611680
max_val = numpy.log10(max_val if max_val > 0 else 1)
612681

613-
if bpm.autoscale or bpm.lut_method=="LOG":
614-
scaling = (2**16 - 1.) / (max_val - min_val)
615-
scale_image = ((scale_image - min_val) * scaling).astype(numpy.uint16)
616-
else:
617-
shift = bpp - 16
618-
if shift > 0:
619-
scale_image = numpy.right_shift(scale_image, shift).astype(numpy.uint16)
620-
elif shift < 0:
621-
scale_image = numpy.left_shift(scale_image, -shift).astype(numpy.uint16)
622-
else:
623-
scale_image = scale_image.astype(numpy.uint16)
682+
# scale the image to the whole range 16bit before palette transformation for 0 to 65535
683+
scaling = (2**16 - 1.) / (max_val - min_val)
684+
scale_image = ((scale_image - min_val) * scaling).astype(numpy.uint16)
624685

625-
if bpm.color_map==True:
686+
# last transformation ot color or greys palette
687+
if bpm.color_map:
626688
img_buffer = bpm.palette["color"].take(scale_image, axis=0)
627689
else:
628690
img_buffer = bpm.palette["grey"].take(scale_image, axis=0)
@@ -635,9 +697,17 @@ def construct_bvdata(bpm):
635697

636698
raw_jpeg_data = jpegFile.getvalue()
637699
image_jpeg = base64.b64encode(raw_jpeg_data)
638-
profil_x = last_proj_x.tobytes()
639-
profil_y = last_proj_y.tobytes()
640-
bvdata_format='dldddliiiidd%ds%ds%ds' %(len(profil_x),len(profil_y),len(image_jpeg))
700+
if bpm.return_bpm_profiles:
701+
profile_x = last_proj_x.tobytes()
702+
profile_y = last_proj_y.tobytes()
703+
else:
704+
profile_x = image.buffer[:,bpm.beammark[0]].astype(numpy.uint64)
705+
profile_x = profile_x.tobytes()
706+
profile_y = image.buffer[bpm.beammark[1],:].astype(numpy.uint64)
707+
profile_y = profile_y.tobytes()
708+
709+
710+
bvdata_format='dldddliiiidd%ds%ds%ds' %(len(profile_x),len(profile_y),len(image_jpeg))
641711
bvdata = struct.pack(
642712
bvdata_format,
643713
last_acq_time,
@@ -652,8 +722,8 @@ def construct_bvdata(bpm):
652722
roi_size.getHeight(),
653723
last_fwhm_x,
654724
last_fwhm_y,
655-
profil_x,
656-
profil_y,
725+
profile_x,
726+
profile_y,
657727
image_jpeg)
658728
return bvdata, bvdata_format
659729

0 commit comments

Comments
 (0)