| Total Complexity | 70 |
| Total Lines | 473 |
| Duplicated Lines | 7.4 % |
| Changes | 0 | ||
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like savu.plugins.centering.vo_centering_iterative often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | # Copyright 2014 Diamond Light Source Ltd. |
||
| 2 | # |
||
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
||
| 4 | # you may not use this file except in compliance with the License. |
||
| 5 | # You may obtain a copy of the License at |
||
| 6 | # |
||
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
||
| 8 | # |
||
| 9 | # Unless required by applicable law or agreed to in writing, software |
||
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
||
| 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
| 12 | # See the License for the specific language governing permissions and |
||
| 13 | # limitations under the License. |
||
| 14 | """ |
||
| 15 | .. module:: vo_centering_iterative |
||
| 16 | :platform: Unix |
||
| 17 | :synopsis: A plugin to find the center of rotation per frame |
||
| 18 | .. moduleauthor:: Mark Basham <[email protected]> |
||
| 19 | """ |
||
| 20 | |||
| 21 | import math |
||
| 22 | import logging |
||
| 23 | import numpy as np |
||
| 24 | import scipy.ndimage as ndi |
||
| 25 | import scipy.ndimage.filters as filter |
||
| 26 | import pyfftw.interfaces.scipy_fftpack as fft |
||
| 27 | |||
| 28 | from scipy import signal |
||
| 29 | |||
| 30 | from savu.plugins.utils import register_plugin |
||
| 31 | from savu.data.plugin_list import CitationInformation |
||
| 32 | from savu.plugins.filters.base_filter import BaseFilter |
||
| 33 | from savu.plugins.driver.iterative_plugin import IterativePlugin |
||
| 34 | |||
| 35 | # :u*param search_area: Search area in pixels from horizontal approximate \ |
||
| 36 | # centre of the image. Default: (-50, 50). |
||
| 37 | # Deprecated !!! |
||
| 38 | |||
| 39 | @register_plugin |
||
| 40 | class VoCenteringIterative(BaseFilter, IterativePlugin): |
||
| 41 | """ |
||
| 42 | A plugin to calculate the centre of rotation using the Vo Method |
||
| 43 | |||
| 44 | :param ratio: The ratio between the size of object and FOV of \ |
||
| 45 | the camera. Default: 0.5. |
||
| 46 | :param row_drop: Drop lines around vertical center of the \ |
||
| 47 | mask. Default: 20. |
||
| 48 | :param search_radius: Use for fine searching. Default: 6. |
||
| 49 | :param step: Step of fine searching. Default: 0.5. |
||
| 50 | :param expand_by: The number of pixels to expand the search region by \ |
||
| 51 | on each iteration. Default: 5 |
||
| 52 | :param boundary_distance: Accepted distance of minima from the boundary of\ |
||
| 53 | the listshift in the coarse search. Default: 3. |
||
| 54 | :u*param preview: A slice list of required frames (sinograms) to use in \ |
||
| 55 | the calulation of the centre of rotation (this will not reduce the data \ |
||
| 56 | size for subsequent plugins). Default: []. |
||
| 57 | :param datasets_to_populate: A list of datasets which require this \ |
||
| 58 | information. Default: []. |
||
| 59 | :param out_datasets: The default \ |
||
| 60 | names. Default: ['cor_raw','cor_fit', 'reliability']. |
||
| 61 | :u*param start_pixel: The approximate centre. If value is None, take the \ |
||
| 62 | value from .nxs file else set to image centre. Default: None. |
||
| 63 | """ |
||
| 64 | |||
| 65 | def __init__(self): |
||
| 66 | super(VoCenteringIterative, self).__init__("VoCenteringIterative") |
||
| 67 | self.search_area = (-20, 20) |
||
| 68 | self.peak_height_min = 50000 # arbitrary |
||
| 69 | self.min_dist = 3 # min distance deamed acceptible from boundary |
||
| 70 | self.expand_by = 5 # expand the search region by this amount |
||
| 71 | self.list_shift = None |
||
| 72 | self.warning_level = 0 |
||
| 73 | self.final = False |
||
| 74 | self.at_boundary = False |
||
| 75 | self.list_metric = [] |
||
| 76 | self.expand_direction = None |
||
| 77 | |||
| 78 | def _create_mask(self, Nrow, Ncol, obj_radius): |
||
| 79 | du, dv = 1.0/Ncol, (Nrow-1.0)/(Nrow*2.0*math.pi) |
||
| 80 | cen_row, cen_col = int(np.ceil(Nrow / 2.0)-1), int(np.ceil(Ncol / 2.0)-1) |
||
| 81 | drop = self.parameters['row_drop'] |
||
| 82 | mask = np.zeros((Nrow, Ncol), dtype=np.float32) |
||
| 83 | for i in range(Nrow): |
||
| 84 | num1 = np.round(((i-cen_row)*dv/obj_radius)/du) |
||
| 85 | p1, p2 = (np.clip(np.sort((-num1+cen_col, num1+cen_col)), |
||
| 86 | 0, Ncol-1)).astype(int) |
||
| 87 | mask[i, p1:p2+1] = np.ones(p2-p1+1, dtype=np.float32) |
||
| 88 | |||
| 89 | if drop < cen_row: |
||
| 90 | mask[cen_row-drop:cen_row+drop+1, :] = \ |
||
| 91 | np.zeros((2*drop + 1, Ncol), dtype=np.float32) |
||
| 92 | mask[:, cen_col-1:cen_col+2] = np.zeros((Nrow, 3), dtype=np.float32) |
||
| 93 | return mask |
||
| 94 | |||
| 95 | def _get_start_shift(self, centre): |
||
| 96 | if self.parameters['start_pixel'] is not None: |
||
| 97 | shift = centre - int(self.parameters['start_pixel']/self.downlevel) |
||
| 98 | else: |
||
| 99 | in_mData = self.get_in_meta_data()[0] |
||
| 100 | shift = centre - in_mData['centre'] if 'centre' in \ |
||
| 101 | list(in_mData.get_dictionary().keys()) else 0 |
||
| 102 | return int(shift) |
||
| 103 | |||
| 104 | def _coarse_search(self, sino, list_shift): |
||
| 105 | # search minsearch to maxsearch in 1 pixel steps |
||
| 106 | list_metric = np.zeros(len(list_shift), dtype=np.float32) |
||
| 107 | (Nrow, Ncol) = sino.shape |
||
| 108 | # check angles to determine if a sinogram should be chopped off. |
||
| 109 | # Copy the sinogram and flip left right, to make a full [0:2Pi] sino |
||
| 110 | sino2 = np.fliplr(sino[1:]) |
||
| 111 | # This image is used for compensating the shift of sino2 |
||
| 112 | compensateimage = np.zeros((Nrow-1, Ncol), dtype=np.float32) |
||
| 113 | # Start coarse search in which the shift step is 1 |
||
| 114 | compensateimage[:] = np.flipud(sino)[1:] |
||
| 115 | mask = self._create_mask(2*Nrow-1, Ncol, |
||
| 116 | 0.5*self.parameters['ratio']*Ncol) |
||
| 117 | count = 0 |
||
| 118 | for i in list_shift: |
||
| 119 | sino2a = np.roll(sino2, i, axis=1) |
||
| 120 | if i >= 0: |
||
| 121 | sino2a[:, 0:i] = compensateimage[:, 0:i] |
||
| 122 | else: |
||
| 123 | sino2a[:, i:] = compensateimage[:, i:] |
||
| 124 | list_metric[count] = np.sum( |
||
| 125 | np.abs(fft.fftshift(fft.fft2(np.vstack((sino, sino2a)))))*mask) |
||
| 126 | count += 1 |
||
| 127 | return list_metric |
||
| 128 | |||
| 129 | def _fine_search(self, sino, raw_cor): |
||
| 130 | (Nrow, Ncol) = sino.shape |
||
| 131 | centerfliplr = (Ncol + 1.0) / 2.0 - 1.0 |
||
| 132 | # Use to shift the sino2 to the raw CoR |
||
| 133 | shiftsino = np.int16(2*(raw_cor-centerfliplr)) |
||
| 134 | sino2 = np.roll(np.fliplr(sino[1:]), shiftsino, axis=1) |
||
| 135 | lefttake = 0 |
||
| 136 | righttake = Ncol-1 |
||
| 137 | search_rad = self.parameters['search_radius'] |
||
| 138 | |||
| 139 | if raw_cor <= centerfliplr: |
||
| 140 | lefttake = np.int16(np.ceil(search_rad+1)) |
||
| 141 | righttake = np.int16(np.floor(2*raw_cor-search_rad-1)) |
||
| 142 | else: |
||
| 143 | lefttake = np.int16(np.ceil(raw_cor-(Ncol-1-raw_cor)+search_rad+1)) |
||
| 144 | righttake = np.int16(np.floor(Ncol-1-search_rad-1)) |
||
| 145 | |||
| 146 | Ncol1 = righttake-lefttake + 1 |
||
| 147 | mask = self._create_mask(2*Nrow-1, Ncol1, |
||
| 148 | 0.5*self.parameters['ratio']*Ncol) |
||
| 149 | numshift = np.int16((2*search_rad)/self.parameters['step'])+1 |
||
| 150 | listshift = np.linspace(-search_rad, search_rad, num=numshift) |
||
| 151 | listmetric = np.zeros(len(listshift), dtype=np.float32) |
||
| 152 | num1 = 0 |
||
| 153 | factor1 = np.mean(sino[-1, lefttake:righttake]) |
||
| 154 | for i in listshift: |
||
| 155 | sino2a = ndi.interpolation.shift(sino2, (0, i), prefilter=False) |
||
| 156 | factor2 = np.mean(sino2a[0, lefttake:righttake]) |
||
| 157 | sino2a = sino2a*factor1/factor2 |
||
| 158 | sinojoin = np.vstack((sino, sino2a)) |
||
| 159 | listmetric[num1] = np.sum(np.abs(fft.fftshift( |
||
| 160 | fft.fft2(sinojoin[:, lefttake:righttake + 1])))*mask) |
||
| 161 | num1 = num1 + 1 |
||
| 162 | minpos = np.argmin(listmetric) |
||
| 163 | rotcenter = raw_cor + listshift[minpos] / 2.0 |
||
| 164 | return rotcenter |
||
| 165 | |||
| 166 | def _get_listshift(self): |
||
| 167 | smin, smax = self.search_area if self.get_iteration() == 0 \ |
||
| 168 | else self._expand_search() |
||
| 169 | list_shift = np.arange(smin, smax+2, 2) - self.start_shift |
||
| 170 | logging.debug('list shift is %s', list_shift) |
||
| 171 | return list_shift |
||
| 172 | |||
| 173 | def _expand_search(self): |
||
| 174 | if self.expand_direction == 'left': |
||
| 175 | return self._expand_left() |
||
| 176 | elif self.expand_direction == 'right': |
||
| 177 | return self._expand_right() |
||
| 178 | else: |
||
| 179 | raise Exception('Unknown expand direction.') |
||
| 180 | |||
| 181 | def _expand_left(self): |
||
| 182 | smax = self.list_shift[0] - 2 |
||
| 183 | smin = smax - self.expand_by*2 |
||
| 184 | |||
| 185 | if smin <= -self.boundary: |
||
| 186 | smin = -self.boundary |
||
| 187 | self.at_boundary = True |
||
| 188 | return smin, smax |
||
| 189 | |||
| 190 | def _expand_right(self): |
||
| 191 | smin = self.list_shift[-1] + 2 |
||
| 192 | smax = self.list_shift[-1] + self.expand_by*2 |
||
| 193 | |||
| 194 | if smax <= self.boundary: |
||
| 195 | smax = self.boundary |
||
| 196 | self.at_boundary = True |
||
| 197 | |||
| 198 | return smin, smax |
||
| 199 | |||
| 200 | def pre_process(self): |
||
| 201 | pData = self.get_plugin_in_datasets()[0] |
||
| 202 | label = pData.get_data_dimension_by_axis_label |
||
| 203 | Ncol = pData.get_shape()[label('detector_x')] |
||
| 204 | self.downlevel = 4 if Ncol > 1800 else 1 |
||
| 205 | self.downsample = slice(0, Ncol, self.downlevel) |
||
| 206 | Ncol_downsample = len(np.arange(0, Ncol, self.downlevel)) |
||
| 207 | self.centre_fliplr = (Ncol_downsample - 1.0) / 2.0 |
||
| 208 | self.start_shift = self._get_start_shift(self.centre_fliplr)*2 |
||
| 209 | self.boundary = int(np.ceil(Ncol/4.0)) |
||
| 210 | |||
| 211 | def process_frames(self, data): |
||
| 212 | if not self.final: |
||
| 213 | logging.debug('performing coarse search for iteration %s', |
||
| 214 | self.get_iteration()) |
||
| 215 | sino = filter.gaussian_filter(data[0][:, self.downsample], (3, 1)) |
||
| 216 | list_shift = self._get_listshift() |
||
| 217 | list_metric = self._coarse_search(sino, list_shift) |
||
| 218 | self._update_lists(list(list_shift), list(list_metric)) |
||
| 219 | |||
| 220 | self.coarse_cor, dist, reliability_metrics = \ |
||
| 221 | self._analyse_result(self.list_metric, self.list_shift) |
||
| 222 | |||
| 223 | return [np.array([self.coarse_cor]), np.array([dist]), |
||
| 224 | np.array([reliability_metrics]), np.array([self.list_metric])] |
||
| 225 | else: |
||
| 226 | logging.debug("performing fine search") |
||
| 227 | sino = filter.median_filter(data[0], (2, 2)) |
||
| 228 | cor = self._fine_search(sino, self.coarse_cor) |
||
| 229 | self.set_processing_complete() |
||
| 230 | return [np.array([cor]), np.array([self.list_metric])] |
||
| 231 | |||
| 232 | def _update_lists(self, shift, metric): |
||
| 233 | if self.expand_direction == 'left': |
||
| 234 | self.list_shift = shift + self.list_shift |
||
| 235 | self.list_metric = metric + self.list_metric |
||
| 236 | elif self.expand_direction == 'right': |
||
| 237 | self.list_shift += shift |
||
| 238 | self.list_metric += metric |
||
| 239 | else: |
||
| 240 | self.list_shift = shift |
||
| 241 | self.list_metric = metric |
||
| 242 | |||
| 243 | def _analyse_result(self, metric, shift): |
||
| 244 | minpos = np.argmin(metric) |
||
| 245 | dist = min(abs(len(shift) - minpos), -minpos) |
||
| 246 | |||
| 247 | rot_centre = (self.centre_fliplr + shift[minpos] / 2.0)*self.downlevel |
||
| 248 | peaks = self._find_peaks(metric) |
||
| 249 | |||
| 250 | good_nPeaks = True |
||
| 251 | if len(peaks) != 1: |
||
| 252 | good_nPeaks = False |
||
| 253 | good_peak_height = True if np.any(peaks) and \ |
||
| 254 | max(peaks) > self.peak_height_min else False |
||
| 255 | |||
| 256 | metric = 0.0 |
||
| 257 | if (good_peak_height and good_nPeaks): |
||
| 258 | metric = 1.0 |
||
| 259 | elif (good_peak_height or good_nPeaks): |
||
| 260 | metric = 0.5 |
||
| 261 | |||
| 262 | return rot_centre, dist, metric |
||
| 263 | |||
| 264 | def _find_peaks(self, metric): |
||
| 265 | import peakutils |
||
| 266 | grad2 = np.gradient(np.gradient(metric)) |
||
| 267 | grad2[grad2 < 0] = 0 |
||
| 268 | index = peakutils.indexes(grad2, thres=0.5, min_dist=3) |
||
| 269 | return np.sort(grad2[index]) |
||
| 270 | |||
| 271 | def post_process(self): |
||
| 272 | logging.debug("in the post process function") |
||
| 273 | in_datasets, out_datasets = self.get_datasets() |
||
| 274 | |||
| 275 | # ===================================================================== |
||
| 276 | # Analyse distance of centre values from boundary of search region |
||
| 277 | dist_from_boundary = np.squeeze(out_datasets[1].data[...]) |
||
| 278 | near_boundary = np.where(abs(dist_from_boundary) < self.min_dist)[0] |
||
| 279 | nEntries = len(dist_from_boundary) |
||
| 280 | |||
| 281 | # Case1: Greater than half the results are near the boundary |
||
| 282 | if (len(near_boundary)/float(nEntries)) > 0.5: |
||
| 283 | # find which boundary |
||
| 284 | signs = np.sign(dist_from_boundary[near_boundary]) |
||
| 285 | left, right = len(signs[signs < 0]), len(signs[signs > 0]) |
||
| 286 | |||
| 287 | logging.debug("res: results are near boundary") |
||
| 288 | if not self.at_boundary: |
||
| 289 | # if they are all at the same boundary expand the search region |
||
| 290 | if not (left and right): |
||
| 291 | logging.debug("res: expanding") |
||
| 292 | self.expand_direction = 'left' if left else 'right' |
||
| 293 | # if they are at different boundaries determine which values |
||
| 294 | # are most reliable |
||
| 295 | else: |
||
| 296 | logging.debug("res: choosing a boundary") |
||
| 297 | self.expand_direction = \ |
||
| 298 | self._choose_boundary(near_boundary, signs) |
||
| 299 | # case that the results are close to different boundaries |
||
| 300 | # Analyse reliability and choose direction |
||
| 301 | else: |
||
| 302 | logging.debug("res: at the edge of the boundary") |
||
| 303 | # Move on to the fine search |
||
| 304 | self._set_final_process() |
||
| 305 | self.warning_level = 1 # change this to be more descriptive *** |
||
| 306 | else: |
||
| 307 | logging.debug("result is not near the boundary") |
||
| 308 | # Move on to the fine search |
||
| 309 | self._set_final_process() |
||
| 310 | # ===================================================================== |
||
| 311 | |||
| 312 | def _choose_boundary(self, idx, signs): |
||
| 313 | good, maybe, bad = self._get_reliability_levels() |
||
| 314 | sign = self._check_entries(good, signs[good]) |
||
| 315 | self.warning_level = 0 |
||
| 316 | if not sign: |
||
| 317 | sign = self._check_entries(maybe, signs[maybe]) |
||
| 318 | self.warning_level = 1 |
||
| 319 | if not sign: |
||
| 320 | sign = self._check_entries(bad, signs[bad]) |
||
| 321 | self.warning_level = 2 |
||
| 322 | return sign |
||
| 323 | |||
| 324 | def _check_entries(self, idx, signs): |
||
| 325 | if np.any(idx): |
||
| 326 | left, right = signs[signs < 0], signs[signs > 0] |
||
| 327 | if not (left and right): |
||
| 328 | # use all the good ones |
||
| 329 | return 'left' if left else 'right' |
||
| 330 | return None |
||
| 331 | |||
| 332 | def _get_reliability_levels(self, final=False): |
||
| 333 | in_datasets, out_datasets = \ |
||
| 334 | self.get_datasets() if not final else self.get_original_datasets() |
||
| 335 | reliability = np.squeeze(out_datasets[2].data[...]) |
||
| 336 | logging.debug('reliability is %s', reliability) |
||
| 337 | good = np.where(reliability == 1.0)[0] |
||
| 338 | maybe = np.where(reliability == 0.5)[0] |
||
| 339 | bad = np.where(reliability == 0.0)[0] |
||
| 340 | return good, maybe, bad |
||
| 341 | |||
| 342 | def final_post_process(self): |
||
| 343 | |||
| 344 | # choose which values to include |
||
| 345 | good, maybe, bad = self._get_reliability_levels(final=True) |
||
| 346 | # Do I need to change the warning levels here? |
||
| 347 | entries = good if np.any(good) else maybe if np.any(maybe) else bad |
||
| 348 | self.warning_level = 0 if np.any(good) else 1 if np.any(maybe) else 2 |
||
| 349 | logging.debug('sinograms used in final calculations are %s', entries) |
||
| 350 | |||
| 351 | # do some curve fitting here |
||
| 352 | # Get a handle on the original datasets |
||
| 353 | in_dataset, out_dataset = self.get_original_datasets() |
||
| 354 | cor_raw = np.squeeze(out_dataset[0].data[...])[entries] |
||
| 355 | cor_fit = out_dataset[1].data[...] |
||
| 356 | fit = np.zeros(cor_fit.shape) |
||
| 357 | fit[:] = np.median(cor_raw) |
||
| 358 | cor_fit = fit |
||
| 359 | out_dataset[1].data[:] = cor_fit[:] |
||
| 360 | |||
| 361 | self.populate_meta_data('cor_raw', cor_raw) |
||
| 362 | self.populate_meta_data('centre_of_rotation', |
||
| 363 | out_dataset[1].data[:].squeeze(axis=1)) |
||
| 364 | |||
| 365 | def _set_final_process(self): |
||
| 366 | self.final = True |
||
| 367 | self.post_process = self.final_post_process |
||
| 368 | in_dataset, out_dataset = self.get_datasets() |
||
| 369 | self.set_iteration_datasets( |
||
| 370 | self.get_iteration()+1, [in_dataset[0]], [out_dataset[0]]) |
||
| 371 | |||
| 372 | def populate_meta_data(self, key, value): |
||
| 373 | datasets = self.parameters['datasets_to_populate'] |
||
| 374 | in_meta_data = self.get_in_meta_data()[0] |
||
| 375 | in_meta_data.set(key, value) |
||
| 376 | for name in datasets: |
||
| 377 | self.exp.index['in_data'][name].meta_data.set(key, value) |
||
| 378 | |||
| 379 | def setup(self): |
||
| 380 | # set up the output dataset that is created by the plugin |
||
| 381 | in_dataset, out_dataset = self.get_datasets() |
||
| 382 | |||
| 383 | self.orig_full_shape = in_dataset[0].get_shape() |
||
| 384 | |||
| 385 | # reduce the data as per data_subset parameter |
||
| 386 | self.set_preview(in_dataset[0], self.parameters['preview']) |
||
| 387 | |||
| 388 | in_pData, out_pData = self.get_plugin_datasets() |
||
| 389 | in_pData[0].plugin_data_setup('SINOGRAM', self.get_max_frames()) |
||
| 390 | # copy all required information from in_dataset[0] |
||
| 391 | fullData = in_dataset[0] |
||
| 392 | |||
| 393 | slice_dirs = np.array(in_dataset[0].get_slice_dimensions()) |
||
| 394 | new_shape = (np.prod(np.array(fullData.get_shape())[slice_dirs]), 1) |
||
| 395 | self.orig_shape = \ |
||
| 396 | (np.prod(np.array(self.orig_full_shape)[slice_dirs]), 1) |
||
| 397 | |||
| 398 | self._create_metadata_dataset(out_dataset[0], new_shape) |
||
| 399 | self._create_metadata_dataset(out_dataset[1], self.orig_shape) |
||
| 400 | self._create_metadata_dataset(out_dataset[2], new_shape) |
||
| 401 | |||
| 402 | # output metric |
||
| 403 | new_shape = (np.prod(np.array(fullData.get_shape())[slice_dirs]), 21) |
||
| 404 | self._create_metadata_dataset(out_dataset[3], new_shape) |
||
| 405 | |||
| 406 | out_pData[0].plugin_data_setup('METADATA', self.get_max_frames()) |
||
| 407 | out_pData[1].plugin_data_setup('METADATA', self.get_max_frames()) |
||
| 408 | out_pData[2].plugin_data_setup('METADATA', self.get_max_frames()) |
||
| 409 | out_pData[3].plugin_data_setup('METADATA', self.get_max_frames()) |
||
| 410 | |||
| 411 | def _create_metadata_dataset(self, data, shape): |
||
| 412 | data.create_dataset(shape=shape, |
||
| 413 | axis_labels=['x.pixels', 'y.pixels'], |
||
| 414 | remove=True, |
||
| 415 | transport='hdf5') |
||
| 416 | data.add_pattern("METADATA", core_dims=(1,), slice_dims=(0,)) |
||
| 417 | |||
| 418 | def nOutput_datasets(self): |
||
| 419 | return 4 |
||
| 420 | |||
| 421 | def get_max_frames(self): |
||
| 422 | return 'single' |
||
| 423 | |||
| 424 | def fix_transport(self): |
||
| 425 | # This plugin requires communication between processes in the post |
||
| 426 | # process, which it does via files |
||
| 427 | return 'hdf5' |
||
| 428 | |||
| 429 | def executive_summary(self): |
||
| 430 | if self.warning_level == 0: |
||
| 431 | msg = "Confidence in the centre value is high." |
||
| 432 | elif self.warning_level == 1: |
||
| 433 | msg = "Confidence in the centre value is average." |
||
| 434 | else: |
||
| 435 | msg = "Confidence in the centre value is low." |
||
| 436 | return [msg] |
||
| 437 | |||
| 438 | View Code Duplication | def get_citation_information(self): |
|
|
|
|||
| 439 | cite_info = CitationInformation() |
||
| 440 | cite_info.description = \ |
||
| 441 | ("The center of rotation for this reconstruction was calculated " + |
||
| 442 | "automatically using the method described in this work") |
||
| 443 | cite_info.bibtex = \ |
||
| 444 | ("@article{vo2014reliable,\n" + |
||
| 445 | "title={Reliable method for calculating the center of rotation " + |
||
| 446 | "in parallel-beam tomography},\n" + |
||
| 447 | "author={Vo, Nghia T and Drakopoulos, Michael and Atwood, " + |
||
| 448 | "Robert C and Reinhard, Christina},\n" + |
||
| 449 | "journal={Optics Express},\n" + |
||
| 450 | "volume={22},\n" + |
||
| 451 | "number={16},\n" + |
||
| 452 | "pages={19078--19086},\n" + |
||
| 453 | "year={2014},\n" + |
||
| 454 | "publisher={Optical Society of America}\n" + |
||
| 455 | "}") |
||
| 456 | cite_info.endnote = \ |
||
| 457 | ("%0 Journal Article\n" + |
||
| 458 | "%T Reliable method for calculating the center of rotation in " + |
||
| 459 | "parallel-beam tomography\n" + |
||
| 460 | "%A Vo, Nghia T\n" + |
||
| 461 | "%A Drakopoulos, Michael\n" + |
||
| 462 | "%A Atwood, Robert C\n" + |
||
| 463 | "%A Reinhard, Christina\n" + |
||
| 464 | "%J Optics Express\n" + |
||
| 465 | "%V 22\n" + |
||
| 466 | "%N 16\n" + |
||
| 467 | "%P 19078-19086\n" + |
||
| 468 | "%@ 1094-4087\n" + |
||
| 469 | "%D 2014\n" + |
||
| 470 | "%I Optical Society of America") |
||
| 471 | cite_info.doi = "https://doi.org/10.1364/OE.22.019078" |
||
| 472 | return cite_info |
||
| 473 |