def test_initialization(self): '''assert all parameters are correctly set up when parameters passed in''' my_data_2d = np.array([[1, 2, 3], [1, 2, 3]]) o_calculate = CalculateRadialProfile(data=my_data_2d) assert (o_calculate.data == my_data_2d).all() my_data_3d = np.array([[1, 2, 3], [1, 2, 3], [0, 0, 0]]) o_calculate = CalculateRadialProfile(data=my_data_3d) assert (o_calculate.data == my_data_3d).all() bad_dimension_data = np.array([1, 2, 3]) self.assertRaises(ValueError, CalculateRadialProfile, bad_dimension_data)
def test_report_size_of_image(self): '''assert the size of the array is correctly retrieved''' data = np.ones((20, 10)) [height, width] = np.shape(data) [y0, x0] = [int(height / 2), int(width / 2)] center = (x0, y0) angle_range = (0, 90) o_calculate = CalculateRadialProfile(data=data) o_calculate.add_params(center=center, angle_range=angle_range) assert 10 == o_calculate.x_len assert 20 == o_calculate.y_len
def calculate(self, center={}, angle_range={}): QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.center = center self.angle_range = angle_range nbr_files = len(self.working_data) self.parent_ui.eventProgress.setMinimum(1) self.parent_ui.eventProgress.setMaximum(nbr_files) self.parent_ui.eventProgress.setValue(1) self.parent_ui.eventProgress.setVisible(True) QtGui.QGuiApplication.processEvents() _array_profile = [] self.parent_ui.ui.profile_plot.clear() try: self.parent_ui.ui.profile_plot.scene().removeItem( self.parent_ui.legend) except Exception as e: pass self.parent_ui.legend = self.parent_ui.ui.profile_plot.addLegend() QtGui.QGuiApplication.processEvents() for _index in np.arange(nbr_files): o_calculation = CalculateRadialProfile( data=self.working_data[_index], center=center, angle_range=angle_range) o_calculation.calculate() _short_file_name = self.short_list_files[_index] _profile = o_calculation.radial_profile _array_profile.append(_profile) self.parent_ui.eventProgress.setValue(_index + 1) # display _color = self.list_rgb_profile_color[_index] self.plot(_profile, _short_file_name, _color) QtGui.QGuiApplication.processEvents() QtGui.QGuiApplication.processEvents() self.parent_ui.eventProgress.setVisible(False) self.profile_data = _array_profile QApplication.restoreOverrideCursor()
def test_initialization_real_case(self): '''assert all parameters are correctly set up when real parameters are passed in''' assert os.path.exists(self.data_path) data = self.data [height, width] = np.shape(data) [y0, x0] = [int(height / 2), int(width / 2)] center = (x0, y0) angle_range = (0, 90) o_calculate = CalculateRadialProfile(data=data) o_calculate.add_params(center=center, angle_range=angle_range) assert (o_calculate.data == data).all() assert o_calculate.center == center assert o_calculate.angle_range == angle_range assert o_calculate.x0 == center[0] assert o_calculate.y0 == center[1]
def test_throwing_error_when_center_format_has_wrong_format(self): '''assert error is thrown when center does not have the right format''' o_calculate = CalculateRadialProfile(data=self.data) bad_center = (10, -1) self.assertRaises(ValueError, o_calculate.add_params, bad_center) bad_center = {10, 1} self.assertRaises(AssertionError, o_calculate.add_params, bad_center) bad_center = [10, 1] self.assertRaises(AssertionError, o_calculate.add_params, bad_center) bad_center = (10, 1, 3) self.assertRaises(ValueError, o_calculate.add_params, bad_center)
def test_throwing_error_when_angle_range_has_wrong_format(self): '''assert error is thrown when angle range does not have the right format''' o_calculate = CalculateRadialProfile(data=self.data) bad_angle_range = (-10, 40) self.assertRaises(ValueError, o_calculate.add_params, bad_angle_range) bad_angle_range = {0, 90} self.assertRaises(AssertionError, o_calculate.add_params, bad_angle_range) bad_angle_range = [0, 90] self.assertRaises(AssertionError, o_calculate.add_params, bad_angle_range) bad_angle_range = (90, 180, 270) self.assertRaises(ValueError, o_calculate.add_params, bad_angle_range)
def test_working_data_correctly_sorted(self): '''assert the working data are correctly sorted according to the pixel radius''' data = np.ones((10, 10)) data[5][4] = 100 [height, width] = np.shape(data) [y0, x0] = [int(height / 2), int(width / 2)] center = (x0, y0) angle_range = None o_calculate = CalculateRadialProfile(data=data) o_calculate.add_params(center=center, angle_range=angle_range) o_calculate.calculate() data_sorted_by_radius = o_calculate.data_sorted_by_radius assert ([1., 1., 100., 1.] == data_sorted_by_radius[:4]).all()
def test_sort_radius(self): '''assert the array of radius is correctly sorted''' data = np.ones((10, 10)) [height, width] = np.shape(data) [y0, x0] = [int(height / 2), int(width / 2)] center = (x0, y0) angle_range = (0, 90) o_calculate = CalculateRadialProfile(data=data) o_calculate.add_params(center=center, angle_range=angle_range) o_calculate.calculate() sorted_radius = o_calculate.sorted_radius assert 1 == sorted_radius[0] # pixel 0 is never used self.assertAlmostEqual(6.4031, sorted_radius[-1], delta=0.01)
def test_sort_indices_of_radius(self): '''assert the array of radius indices is correctly sorted''' data = np.ones((10, 10)) [height, width] = np.shape(data) [y0, x0] = [int(height / 2), int(width / 2)] center = (x0, y0) angle_range = (0, 90) o_calculate = CalculateRadialProfile(data=data) o_calculate.add_params(center=center, angle_range=angle_range) o_calculate.calculate() sorted_radius_indices = o_calculate.sorted_radius_indices assert 55 == sorted_radius_indices[0] assert 0 == sorted_radius_indices[-1]
def test_calculation_of_radius_array(self): '''assert that the array of pixel radius is correct''' data = np.ones((10, 10)) [height, width] = np.shape(data) [y0, x0] = [int(height / 2), int(width / 2)] center = (x0, y0) angle_range = (0, 90) o_calculate = CalculateRadialProfile(data=data) o_calculate.add_params(center=center, angle_range=angle_range) o_calculate.calculate() _radius_array = o_calculate.radius_array assert _radius_array[y0, x0] == 0 # center of circle assert _radius_array[0, 0] == pytest.approx(7.071, abs=0.01)
def test_profile(self): '''assert the final profile''' data = self.data2 [height, width] = np.shape(data) [y0, x0] = [int(height / 2), int(width / 2)] center = (x0, y0) angle_range = (0, 90) o_calculate = CalculateRadialProfile(data=data) o_calculate.add_params(center=center, angle_range=angle_range) o_calculate.calculate() radial_profile = o_calculate.radial_profile radial_profile_array = np.array(radial_profile['mean']) self.assertAlmostEqual(255, radial_profile_array[0], delta=0.0001) self.assertAlmostEqual(255, radial_profile_array[1], delta=0.0001) self.assertAlmostEqual(255, radial_profile_array[2], delta=0.0001) self.assertAlmostEqual(255, radial_profile_array[3], delta=0.0001)
def test_new_working_data(self): '''assert the data outside the angle range are turned off - working data is correct''' data = np.ones((10, 10)) [height, width] = np.shape(data) [y0, x0] = [int(height / 2), int(width / 2)] center = (x0, y0) angle_range = (0, 90) o_calculate = CalculateRadialProfile(data=data) o_calculate.add_params(center=center, angle_range=angle_range) o_calculate.calculate() working_data = o_calculate.working_data real_working_data = np.zeros((10, 10)) real_working_data[:] = np.nan real_working_data[0:6, 5:, ] = 1 real_working_data[y0, x0] = np.nan assert (working_data[0:5, 6:, ] == real_working_data[0:5, 6:, ]).all()
def test_calculate_array_of_angles(self): '''assert the array of angles is correct''' data = np.ones((10, 10)) [height, width] = np.shape(data) [y0, x0] = [int(height / 2), int(width / 2)] center = (x0, y0) angle_range = (0, 90) o_calculate = CalculateRadialProfile(data=data) o_calculate.add_params(center=center, angle_range=angle_range) o_calculate.calculate() array_angle_deg = o_calculate.array_angle_deg assert array_angle_deg[0, 0] == 315 assert array_angle_deg[y0, x0] == 180 assert array_angle_deg[np.int(height / 2), 0] == 270 self.assertAlmostEqual(array_angle_deg[height - 1, 0], 231.3, delta=0.1)
def test_full_radial_profile(self): _file_path = os.path.dirname(__file__) data_path = os.path.abspath( os.path.join(_file_path, '../../notebooks/circle_profile.tif')) data = io.imread(data_path) o_calculate = CalculateRadialProfile(data=data) center = (500, 600) o_calculate.add_params(center=center) o_calculate.calculate() radial_profile = o_calculate.radial_profile radius_returned = radial_profile.index mean_counts_returned = np.array(radial_profile["mean"]) radius_expected = [0, 1.0, 1.4142, 2.0, 2.236, 2.8284] for _expected, _returned in zip(radius_expected, radius_returned): assert _returned == pytest.approx(_expected, abs=1e-2) mean_counts_expected = [500.70709, 501.14412, 501.49767] for _expected, _returned in zip(mean_counts_expected, mean_counts_returned): assert _returned == pytest.approx(_expected, abs=1e-2)
def simple(z, input_folder='stack', output_folder='corrected', angle_ini=-90, angle_fin=90, bit=32): ''' Applies a beam hardening correction on a given tif sequence Correction is calculated on one given slide only --> z = slice to use for correction, z>=1 --> input_folder = folder where the original tif sequence is stored --> output_folder = folder where the corrected tif sequence will be stored --> angle_ini = initial angle for angle range for radial profile angle in degrees: 0 is 12:00 and 90 is 3:00 --> angle_fin = final anle for angle range for radial profile (degrees) ''' file_list = [] for f in os.listdir(input_folder): if f.endswith('.tif'): file_list.append(f) else: for f in os.listdir(input_folder): if f.endswith('.tiff'): file_list.append(f) if not os.path.exists(output_folder): os.makedirs(output_folder) file_list.sort() data_file = input_folder + '/' + file_list[z - 1] # working_data = cv2.imread(data_file, cv2.IMREAD_GRAYSCALE) working_data = cv2.imread(data_file, -1) center = { 'x0': working_data.shape[0] / 2 + 0.5, 'y0': working_data.shape[1] / 2 + 0.5 } #pixels angle_range = {'from': angle_ini, 'to': angle_fin} #degrees r_profile = CalculateRadialProfile(data=working_data, center=center, angle_range=angle_range) r_profile.calculate() profile = r_profile.radial_profile limit = int(np.where(profile == profile.max())[0]) fig1 = plt.figure() plt.plot(profile) i = profile[:limit] r = np.arange(1, len(i) + 1, 1) # Brezier A = {'x': r.min(), 'y': i.min()} C = {'x': r.max(), 'y': i.max()} def funy(w, r, A, C): B = {'x': w[3], 'y': w[4]} h = ((A['x'] - B['x'] + np.sqrt(r * A['x'] - 2 * r * B['x'] + B['x']**2 + r * C['x'] - A['x'] * C['x'])) / (A['x'] - 2 * B['x'] + C['x'])) y = (((1 - h)**2 * A['y'] * w[0] + 2 * (1 - h) * h * B['y'] * w[1] + h**2 * C['y'] * w[2]) / ((1 - h)**2 * w[0] + 2 * (1 - h) * h * w[1] + h**2 * w[2])) return y def offset(w, A, C, r, i): y = funy(w, r, A, C) return i - y wopt = optimize.least_squares( offset, [1, 1, 1, r.max() * 2 / 3, i.min()], args=(A, C, r, i), bounds=([0, 0, 0, 0, 0], [np.inf, np.inf, np.inf, np.inf, np.inf]), verbose=1) ref = [i[int(np.max(r) / 20):int(np.max(r) / 10)].mean()] * len(i) corrected = i - (funy(wopt.x, r, A, C) - ref) fig2 = plt.figure() ax21 = fig2.add_subplot(121) ax21.plot(r, i, label='original') ax21.plot(r, funy(wopt.x, r, A, C), label='fitted with Bezier') ax21.legend() ax22 = fig2.add_subplot(122) ax22.plot(r, i, label='original') ax22.plot(r, corrected, label='corrected') ax22.plot(r, ref, label='aim') ax22.legend() fig3 = plt.figure() ax31 = fig3.add_subplot(121) ax32 = fig3.add_subplot(122) x_range = working_data.shape[0] y_range = working_data.shape[1] new = np.empty(working_data.shape) ref_z = np.full(working_data.shape, ref[0]) @jit def calculate_radii(xrange, yrange, cx, cy): r = np.empty(working_data.shape, np.float64) for x in range(xrange): for y in range(yrange): r[x, y] = np.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)) return r count = 0 radii = calculate_radii(x_range, y_range, center['x0'], center['y0']) for image in file_list: count += 1 if count % 100 == 0: print('Starting slice', count, datetime.datetime.now()) data_file_z = input_folder + '/' + image im_z = cv2.imread(data_file_z, -1) mask = radii <= limit foo = funy(wopt.x, radii * mask, A, C) new = im_z * mask - foo * mask - ref_z * mask os.chdir(output_folder) # new_n = np.float32(new) if bit == 32: new_n = np.float32(new) if bit == 16: I = cv2.normalize(new, None, 0, 65.535, cv2.NORM_MINMAX, cv2.CV_16U) new_n = np.uint16(I) if bit == 8: I = cv2.normalize(new, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U) new_n = np.uint8(I) cv2.imwrite(image, new_n) os.chdir('..') if count == z: corrected = new ax31.imshow(working_data) ax32.imshow(corrected) return new_n