I am writing a scientific software that performs image analysis by measuring beam intensities within circular region of interests (ROIs or spots). The code has a Tracker that allows for following the beams as they move. For each image, there are usually 3-12 spots, and it would be great to have a way to parallelize the part of code that performs the analysis. The relevant code follows:
class Worker(QObject):
""" Worker that manages the spots."""
def __init__(self, spots, center, energy, parent=None):
super(Worker, self).__init__(parent)
#### setup widgets ####
self.plotwid = PlotWidget()
# spots_map:
# - key: spot
# - value: SpotModel, Tracker
self.spots_map = {}
# THIS SHOULD BE PARALLELIZED
for spot in spots:
pos = spot.scenePos()
if center:
tracker = Tracker(pos.x(), pos.y(), spot.radius(), energy, center.x(), center.y(),
input_precision = config.Tracking_inputPrecision,
window_scaling = config.Tracking_windowScalingOn)
else:
tracker = Tracker(pos.x(), pos.y(), spot.radius(), energy,
input_precision = config.Tracking_inputPrecision,
window_scaling = config.Tracking_windowScalingOn)
self.spots_map[spot] = (QSpotModel(self), tracker)
for view, tup in self.spots_map.iteritems():
# view = QGraphicsSpotItem, tup = (QSpotModel, tracker) -> tup[0] = QSpotModel
self.connect(tup[0], SIGNAL("positionChanged"), view.onPositionChange)
self.connect(tup[0], SIGNAL("radiusChanged"), view.onRadiusChange)
This function is called upon every time an image is opened. A tyical acquisition deals with hundreds of images, and for each this funciton is called. Thanks in advance for any help.
Update 1: Thanks for the suggestions below a few tests. First: Multithreading, with same speed as the original:
class thread_it(Thread):
def __init__ (self,param):
Thread.__init__(self)
self.param = param
def run(self):
mutex.acquire()
output.append(calc_stuff(self.param))
mutex.release()
class Worker(QObject):
""" Worker that manages the spots."""
def __init__(self, spots, center, energy, parent=None):
super(Worker, self).__init__(parent)
threads = []
output = []
mutex = thread.allocate_lock()
self.spots_map = {}
for spot in spots:
current = thread_it(self.run(spot, center, energy))
threads.append(current)
current.start()
for t in threads:
t.join()
def run(self, spot, center, energy):
pos = spot.scenePos()
if center:
tracker = Tracker(pos.x(), pos.y(), spot.radius(), energy, center.x(), center.y(),
input_precision = config.Tracking_inputPrecision,
window_scaling = config.Tracking_windowScalingOn)
else:
tracker = Tracker(pos.x(), pos.y(), spot.radius(), energy,
input_precision = config.Tracking_inputPrecision,
window_scaling = config.Tracking_windowScalingOn)
self.spots_map[spot] = (QSpotModel(self), tracker)
This makes no difference in terms of performance.
Update 2: Using joblib:
class Worker(QObject):
""" Worker that manages the spots."""
def __init__(self, spots, center, energy, parent=None):
super(Worker, self).__init__(parent)
self.spots_map = {}
for spot in spots:
Parallel(n_jobs=2)(delayed(self.run)(spot, center, energy) for spot in spots)
for view, tup in self.spots_map.iteritems():
self.connect(tup[0], SIGNAL("positionChanged"), view.onPositionChange)
self.connect(tup[0], SIGNAL("radiusChanged"), view.onRadiusChange)
def run(self, spot, center, energy):
pos = spot.scenePos()
if center:
tracker = Tracker(pos.x(), pos.y(), spot.radius(), energy, center.x(), center.y(),
input_precision = config.Tracking_inputPrecision,
window_scaling = config.Tracking_windowScalingOn)
else:
tracker = Tracker(pos.x(), pos.y(), spot.radius(), energy,
input_precision = config.Tracking_inputPrecision,
window_scaling = config.Tracking_windowScalingOn)
self.spots_map[spot] = (QSpotModel(self), tracker)
Gives this error:
File "/Users/feranick/Desktop/easyleed/source/easyleed/gui.py", line 1189, in __init__
Parallel(n_jobs=2)(delayed(self.run)(spot, center, energy) for spot in spots)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/joblib/parallel.py", line 652, in __call__
for function, args, kwargs in iterable:
File "/Users/feranick/Desktop/easyleed/source/easyleed/gui.py", line 1189, in <genexpr>
Parallel(n_jobs=2)(delayed(self.run)(spot, center, energy) for spot in spots)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/joblib/parallel.py", line 120, in delayed
pickle.dumps(function)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy_reg.py", line 70, in _reduce_ex
raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle instancemethod objects
I am at loss here. Any help is appreciated.