def calculate_zernikes(self, workspace): zernike_indexes = cpmz.get_zernike_indexes(self.zernike_degree.value + 1) meas = workspace.measurements for o in self.objects: object_name = o.object_name.value objects = workspace.object_set.get_objects(object_name) # # First, get a table of centers and radii of minimum enclosing # circles per object # ij = np.zeros((objects.count + 1, 2)) r = np.zeros(objects.count + 1) for labels, indexes in objects.get_labels(): ij_, r_ = minimum_enclosing_circle(labels, indexes) ij[indexes] = ij_ r[indexes] = r_ # # Then compute x and y, the position of each labeled pixel # within a unit circle around the object # ijv = objects.ijv l = ijv[:, 2] yx = (ijv[:, :2] - ij[l, :]) / r[l, np.newaxis] z = cpmz.construct_zernike_polynomials( yx[:, 1], yx[:, 0], zernike_indexes) for image_group in self.images: image_name = image_group.image_name.value image = workspace.image_set.get_image( image_name, must_be_grayscale=True) pixels = image.pixel_data mask = (ijv[:, 0] < pixels.shape[0]) & \ (ijv[:, 1] < pixels.shape[1]) mask[mask] = image.mask[ijv[mask, 0], ijv[mask, 1]] yx_ = yx[mask, :] l_ = l[mask] z_ = z[mask, :] if len(l_) == 0: for i, (n, m) in enumerate(zernike_indexes): ftr = self.get_zernike_magnitude_name(image_name, n, m) meas[object_name, ftr] = np.zeros(0) if self.wants_zernikes == Z_MAGNITUDES_AND_PHASE: ftr = self.get_zernike_phase_name(image_name, n, m) meas[object_name, ftr] = np.zeros(0) continue areas = scind.sum( np.ones(l_.shape, int), labels=l_, index=objects.indices) for i, (n, m) in enumerate(zernike_indexes): vr = scind.sum( pixels[ijv[mask, 0], ijv[mask, 1]] * z_[:, i].real, labels=l_, index=objects.indices) vi = scind.sum( pixels[ijv[mask, 0], ijv[mask, 1]] * z_[:, i].imag, labels=l_, index=objects.indices) magnitude = np.sqrt(vr * vr + vi * vi) / areas ftr = self.get_zernike_magnitude_name(image_name, n, m) meas[object_name, ftr] = magnitude if self.wants_zernikes == Z_MAGNITUDES_AND_PHASE: phase = np.arctan2(vr, vi) ftr = self.get_zernike_phase_name(image_name, n, m) meas[object_name, ftr] = phase
def get_image_from_features(radius, feature_dictionary): '''Reconstruct the intensity image from the zernike features radius - the radius of the minimum enclosing circle feature_dictionary - keys are (n, m) tuples and values are the magnitudes. returns a greyscale image based on the feature dictionary. ''' i, j = np.mgrid[-radius:(radius + 1), -radius:(radius + 1)].astype(float) / radius mask = (i * i + j * j) <= 1 zernike_indexes = np.array(feature_dictionary.keys()) zernike_features = np.array(feature_dictionary.values()) z = construct_zernike_polynomials(j, i, np.abs(zernike_indexes), mask=mask) zn = (2 * zernike_indexes[:, 0] + 2) / ( (zernike_indexes[:, 1] == 0) + 1) / np.pi z = z * zn[np.newaxis, np.newaxis, :] z = z.real * (zernike_indexes[:, 1] >= 0)[np.newaxis, np.newaxis, :] + \ z.imag * (zernike_indexes[:, 1] <= 0)[np.newaxis, np.newaxis, :] return np.sum(z * zernike_features[np.newaxis, np.newaxis, :], 2)
def test_00_00_zeros(self): """Test construct_zernike_polynomials on an empty image""" zi = self.make_zernike_indexes() zf = z.construct_zernike_polynomials(np.zeros((100, 100)), np.zeros((100, 100)), zi) # All zernikes with m!=0 should be zero m_ne_0 = np.array([i for i in range(zi.shape[0]) if zi[i, 1]]) m_eq_0 = np.array([i for i in range(zi.shape[0]) if zi[i, 1] == 0]) self.assertTrue(np.all(zf[:, :, m_ne_0] == 0)) self.assertTrue(np.all(zf[:, :, m_eq_0] != 0)) scores = z.score_zernike(zf, np.array([]), np.zeros((100, 100), int)) self.assertEqual(np.product(scores.shape), 0)
def test_00_00_zeros(self): """Test construct_zernike_polynomials on an empty image""" zi = self.make_zernike_indexes() zf = z.construct_zernike_polynomials(np.zeros((100,100)), np.zeros((100,100)), zi) # All zernikes with m!=0 should be zero m_ne_0 = np.array([i for i in range(zi.shape[0]) if zi[i,1]]) m_eq_0 = np.array([i for i in range(zi.shape[0]) if zi[i,1]==0]) self.assertTrue(np.all(zf[:,:,m_ne_0]==0)) self.assertTrue(np.all(zf[:,:,m_eq_0]!=0)) scores = z.score_zernike(zf, np.array([]), np.zeros((100,100),int)) self.assertEqual(np.product(scores.shape), 0)
def test_01_01_one_object(self): """Test Zernike on one single circle""" zi = self.make_zernike_indexes() y,x = np.mgrid[-50:51,-50:51].astype(float)/50 labels = x**2+y**2 <=1 x[labels==0]=0 y[labels==0]=0 zf = z.construct_zernike_polynomials(x, y, zi) scores = z.score_zernike(zf,[50], labels) # Zernike 0,0 should be 1 and others should be zero within # an approximation of 1/radius epsilon = 1.0/50.0 self.assertTrue(abs(scores[0,0]-1) < epsilon ) self.assertTrue(np.all(scores[0,1:] < epsilon))
def test_01_01_one_object(self): """Test Zernike on one single circle""" zi = self.make_zernike_indexes() y, x = np.mgrid[-50:51, -50:51].astype(float) / 50 labels = x**2 + y**2 <= 1 x[labels == 0] = 0 y[labels == 0] = 0 zf = z.construct_zernike_polynomials(x, y, zi) scores = z.score_zernike(zf, [50], labels) # Zernike 0,0 should be 1 and others should be zero within # an approximation of 1/radius epsilon = 1.0 / 50.0 self.assertTrue(abs(scores[0, 0] - 1) < epsilon) self.assertTrue(np.all(scores[0, 1:] < epsilon))
def score_rotations(self, labels, n): """Score the result of n rotations of the label matrix then test for equality""" self.assertEqual(labels.shape[0], labels.shape[1], "Must use square matrix for test") self.assertEqual(labels.shape[0] & 1, 1, "Must be odd width/height") zi = self.make_zernike_indexes() test_labels = np.zeros((labels.shape[0] * n, labels.shape[0])) test_x = np.zeros((labels.shape[0] * n, labels.shape[0])) test_y = np.zeros((labels.shape[0] * n, labels.shape[0])) diameter = labels.shape[0] radius = labels.shape[0] // 2 y, x = ( np.mgrid[-radius:radius + 1, -radius:radius + 1].astype(float) / radius) anti_mask = x**2 + y**2 > 1 x[anti_mask] = 0 y[anti_mask] = 0 min_pixels = 100000 max_pixels = 0 for i in range(0, n): angle = 360 * i // n # believe it or not, in degrees! off_x = labels.shape[0] * i off_y = 0 rotated_labels = scind.rotate(labels, angle, order=0, reshape=False) pixels = np.sum(rotated_labels) min_pixels = min(min_pixels, pixels) max_pixels = max(max_pixels, pixels) x_mask = x.copy() y_mask = y.copy() x_mask[rotated_labels == 0] = 0 y_mask[rotated_labels == 0] = 0 test_labels[off_x:off_x + diameter, off_y:off_y + diameter] = rotated_labels * (i + 1) test_x[off_x:off_x + diameter, off_y:off_y + diameter] = x_mask test_y[off_x:off_x + diameter, off_y:off_y + diameter] = y_mask zf = z.construct_zernike_polynomials(test_x, test_y, zi) scores = z.score_zernike(zf, np.ones((n, )) * radius, test_labels) score_0 = scores[0] epsilon = 2.0 * (max(1, max_pixels - min_pixels)) / max_pixels for score in scores[1:, :]: self.assertTrue(np.all(np.abs(score - score_0) < epsilon))
def score_rotations(self,labels,n): """Score the result of n rotations of the label matrix then test for equality""" self.assertEqual(labels.shape[0],labels.shape[1],"Must use square matrix for test") self.assertEqual(labels.shape[0] & 1,1,"Must be odd width/height") zi = self.make_zernike_indexes() test_labels = np.zeros((labels.shape[0]*n,labels.shape[0])) test_x = np.zeros((labels.shape[0]*n,labels.shape[0])) test_y = np.zeros((labels.shape[0]*n,labels.shape[0])) diameter = labels.shape[0] radius = labels.shape[0]/2 y,x=np.mgrid[-radius:radius+1,-radius:radius+1].astype(float)/radius anti_mask = x**2+y**2 > 1 x[anti_mask] = 0 y[anti_mask] = 0 min_pixels = 100000 max_pixels = 0 for i in range(0,n): angle = 360*i / n # believe it or not, in degrees! off_x = labels.shape[0]*i off_y = 0 rotated_labels = scind.rotate(labels,angle,order=0,reshape=False) pixels = np.sum(rotated_labels) min_pixels = min(min_pixels,pixels) max_pixels = max(max_pixels,pixels) x_mask = x.copy() y_mask = y.copy() x_mask[rotated_labels==0]=0 y_mask[rotated_labels==0]=0 test_labels[off_x:off_x+diameter, off_y:off_y+diameter] = rotated_labels * (i+1) test_x[off_x:off_x+diameter, off_y:off_y+diameter] = x_mask test_y[off_x:off_x+diameter, off_y:off_y+diameter] = y_mask zf = z.construct_zernike_polynomials(test_x,test_y,zi) scores = z.score_zernike(zf,np.ones((n,))*radius,test_labels) score_0=scores[0] epsilon = 2.0*(max(1,max_pixels-min_pixels))/max_pixels for score in scores[1:,:]: self.assertTrue(np.all(np.abs(score-score_0)<epsilon))
def get_image_from_features(radius, feature_dictionary): '''Reconstruct the intensity image from the zernike features radius - the radius of the minimum enclosing circle feature_dictionary - keys are (n, m) tuples and values are the magnitudes. returns a greyscale image based on the feature dictionary. ''' i, j = np.mgrid[-radius:(radius+1), -radius:(radius+1)].astype(float) / radius mask = (i*i + j*j) <= 1 zernike_indexes = np.array(feature_dictionary.keys()) zernike_features = np.array(feature_dictionary.values()) z = construct_zernike_polynomials( j, i, np.abs(zernike_indexes), mask=mask) zn = (2*zernike_indexes[:, 0] + 2) / ((zernike_indexes[:, 1] == 0) + 1) / np.pi z = z * zn[np.newaxis, np.newaxis, :] z = z.real * (zernike_indexes[:, 1] >= 0)[np.newaxis, np.newaxis, :] +\ z.imag * (zernike_indexes[:, 1] <= 0)[np.newaxis, np.newaxis, :] return np.sum(z * zernike_features[np.newaxis, np.newaxis, :], 2)
def score_scales(self,labels,n): """Score the result of n 3x scalings of the label matrix then test for equality""" self.assertEqual(labels.shape[0],labels.shape[1],"Must use square matrix for test") self.assertEqual(labels.shape[0] & 1,1,"Must be odd width/height") width = labels.shape[0] * 3**n height = width * (n+1) zi = self.make_zernike_indexes() test_labels = np.zeros((height,width)) test_x = np.zeros((height,width)) test_y = np.zeros((height,width)) radii = [] for i in range(n+1): scaled_labels = scind.zoom(labels,3**i,order=0) diameter = scaled_labels.shape[0] radius = scaled_labels.shape[0]/2 radii.append(radius) y,x=np.mgrid[-radius:radius+1,-radius:radius+1].astype(float)/radius anti_mask = x**2+y**2 > 1 x[anti_mask] = 0 y[anti_mask] = 0 off_x = width*i off_y = 0 x[scaled_labels==0]=0 y[scaled_labels==0]=0 test_labels[off_x:off_x+diameter, off_y:off_y+diameter] = scaled_labels * (i+1) test_x[off_x:off_x+diameter, off_y:off_y+diameter] = x test_y[off_x:off_x+diameter, off_y:off_y+diameter] = y zf = z.construct_zernike_polynomials(test_x,test_y,zi) scores = z.score_zernike(zf,np.array(radii),test_labels) score_0=scores[0] epsilon = .02 for score in scores[1:,:]: self.assertTrue(np.all(np.abs(score-score_0)<epsilon))
def score_scales(self, labels, n): """Score the result of n 3x scalings of the label matrix then test for equality""" self.assertEqual(labels.shape[0], labels.shape[1], "Must use square matrix for test") self.assertEqual(labels.shape[0] & 1, 1, "Must be odd width/height") width = labels.shape[0] * 3**n height = width * (n + 1) zi = self.make_zernike_indexes() test_labels = np.zeros((height, width)) test_x = np.zeros((height, width)) test_y = np.zeros((height, width)) radii = [] for i in range(n + 1): scaled_labels = scind.zoom(labels, 3**i, order=0) diameter = scaled_labels.shape[0] radius = scaled_labels.shape[0] // 2 radii.append(radius) y, x = np.mgrid[-radius:radius + 1, -radius:radius + 1].astype(float) / radius anti_mask = x**2 + y**2 > 1 x[anti_mask] = 0 y[anti_mask] = 0 off_x = width * i off_y = 0 x[scaled_labels == 0] = 0 y[scaled_labels == 0] = 0 test_labels[off_x:off_x + diameter, off_y:off_y + diameter] = scaled_labels * (i + 1) test_x[off_x:off_x + diameter, off_y:off_y + diameter] = x test_y[off_x:off_x + diameter, off_y:off_y + diameter] = y zf = z.construct_zernike_polynomials(test_x, test_y, zi) scores = z.score_zernike(zf, np.array(radii), test_labels) score_0 = scores[0] epsilon = .02 for score in scores[1:, :]: self.assertTrue(np.all(np.abs(score - score_0) < epsilon))
def measure_zernike(self, pixels, labels, indexes, centers, radius, n, m): # I'll put some documentation in here to explain what it does. # If someone ever wants to call it, their editor might display # the documentation. '''Measure the intensity of the image with Zernike (N, M) pixels - the intensity image to be measured labels - the labels matrix that labels each object with an integer indexes - the label #s in the image centers - the centers of the minimum enclosing circle for each object radius - the radius of the minimum enclosing circle for each object n, m - the Zernike coefficients. See http://en.wikipedia.org/wiki/Zernike_polynomials for an explanation of the Zernike polynomials ''' # # The strategy here is to operate on the whole array instead # of operating on one object at a time. The most important thing # is to avoid having to run the Python interpreter once per pixel # in the image and the second most important is to avoid running # it per object in case there are hundreds of objects. # # We play lots of indexing tricks here to operate on the whole image. # I'll try to explain some - hopefully, you can reuse. center_x = centers[:, 1] center_y = centers[:, 0] # # Make up fake values for 0 (the background). This lets us do our # indexing tricks. Really, we're going to ignore the background, # but we want to do the indexing without ignoring the background # because that's easier. # center_x = np.hstack([[0], center_x]) center_y = np.hstack([[0], center_y]) radius = np.hstack([[1], radius]) # # Now get one array that's the y coordinate of each pixel and one # that's the x coordinate. This might look stupid and wasteful, # but these "arrays" are never actually realized and made into # real memory. # y, x = np.mgrid[0:labels.shape[0], 0:labels.shape[1]] # # Get the x and y coordinates relative to the object centers. # This uses Numpy broadcasting. For each pixel, we use the # value in the labels matrix as an index into the appropriate # one-dimensional array. So we get the value for that object. # y -= center_y[labels] x -= center_x[labels] # # Zernikes take x and y values from zero to one. We scale the # integer coordinate values by dividing them by the radius of # the circle. Again, we use the indexing trick to look up the # values for each object. # y = y.astype(float) / radius[labels] x = x.astype(float) / radius[labels] # ################################# # # ZERNIKE POLYNOMIALS # # Now we can get Zernike polynomials per-pixel where each pixel # value is calculated according to its object's MEC. # # We use a mask of all of the non-zero labels so the calculation # runs a little faster. # zernike_polynomial = construct_zernike_polynomials( x, y, np.array([[n, m]]), labels > 0) # # For historical reasons, CellProfiler didn't multiply by the per/zernike # normalizing factor: 2*n + 2 / E / pi where E is 2 if m is zero and 1 # if m is one. We do it here to aid with the reconstruction # zernike_polynomial *= (2 * n + 2) / (2 if m == 0 else 1) / np.pi # # Multiply the Zernike polynomial by the image to dissect # the image by the Zernike basis set. # output_pixels = pixels * zernike_polynomial[:, :, 0] # # Finally, we use Scipy to sum the intensities. Scipy has different # versions with different quirks. The "fix" function takes all # of that into account. # # The sum function calculates the sum of the pixel values for # each pixel in an object, using the labels matrix to name # the pixels in an object # zr = fix(scind.sum(output_pixels.real, labels, indexes)) zi = fix(scind.sum(output_pixels.imag, labels, indexes)) # # And we're done! Did you like it? Did you get it? # return zr, zi
def measure_zernike(self, pixels, labels, indexes, centers, radius, n, m): # I'll put some documentation in here to explain what it does. # If someone ever wants to call it, their editor might display # the documentation. '''Measure the intensity of the image with Zernike (N, M) pixels - the intensity image to be measured labels - the labels matrix that labels each object with an integer indexes - the label #s in the image centers - the centers of the minimum enclosing circle for each object radius - the radius of the minimum enclosing circle for each object n, m - the Zernike coefficients. See http://en.wikipedia.org/wiki/Zernike_polynomials for an explanation of the Zernike polynomials ''' # # The strategy here is to operate on the whole array instead # of operating on one object at a time. The most important thing # is to avoid having to run the Python interpreter once per pixel # in the image and the second most important is to avoid running # it per object in case there are hundreds of objects. # # We play lots of indexing tricks here to operate on the whole image. # I'll try to explain some - hopefully, you can reuse. center_x = centers[:, 1] center_y = centers[:, 0] # # Make up fake values for 0 (the background). This lets us do our # indexing tricks. Really, we're going to ignore the background, # but we want to do the indexing without ignoring the background # because that's easier. # center_x = np.hstack([[0], center_x]) center_y = np.hstack([[0], center_y]) radius = np.hstack([[1], radius]) # # Now get one array that's the y coordinate of each pixel and one # that's the x coordinate. This might look stupid and wasteful, # but these "arrays" are never actually realized and made into # real memory. # y, x = np.mgrid[0:labels.shape[0], 0:labels.shape[1]] # # Get the x and y coordinates relative to the object centers. # This uses Numpy broadcasting. For each pixel, we use the # value in the labels matrix as an index into the appropriate # one-dimensional array. So we get the value for that object. # y -= center_y[labels] x -= center_x[labels] # # Zernikes take x and y values from zero to one. We scale the # integer coordinate values by dividing them by the radius of # the circle. Again, we use the indexing trick to look up the # values for each object. # y = y.astype(float) / radius[labels] x = x.astype(float) / radius[labels] # ################################# # # ZERNIKE POLYNOMIALS # # Now we can get Zernike polynomials per-pixel where each pixel # value is calculated according to its object's MEC. # # We use a mask of all of the non-zero labels so the calculation # runs a little faster. # zernike_polynomial = construct_zernike_polynomials( x, y, np.array([ [ n, m ]]), labels > 0) # # For historical reasons, CellProfiler didn't multiply by the per/zernike # normalizing factor: 2*n + 2 / E / pi where E is 2 if m is zero and 1 # if m is one. We do it here to aid with the reconstruction # zernike_polynomial *= (2*n + 2) / (2 if m == 0 else 1) / np.pi # # Multiply the Zernike polynomial by the image to dissect # the image by the Zernike basis set. # output_pixels = pixels * zernike_polynomial[:,:,0] # # Finally, we use Scipy to sum the intensities. Scipy has different # versions with different quirks. The "fix" function takes all # of that into account. # # The sum function calculates the sum of the pixel values for # each pixel in an object, using the labels matrix to name # the pixels in an object # zr = fix(scind.sum(output_pixels.real, labels, indexes)) zi = fix(scind.sum(output_pixels.imag, labels, indexes)) # # And we're done! Did you like it? Did you get it? # return zr, zi