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, 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 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. # # You could move the calculation of the minimum enclosing circle # outside of this function. The function gets called more than # 10 times, so the same calculation is performed 10 times. # It would make the code a little more confusing, so I'm leaving # it as-is. ########################################### # # The minimum enclosing circle (MEC) is the smallest circle that # will fit around the object. We get the centers and radii of # all of the objects at once. You'll see how that lets us # compute the X and Y position of each pixel in a label all at # one go. # # First, get an array that lists the whole range of indexes in # the labels matrix. # indexes = np.arange(1, np.max(labels)+1,dtype=np.int32) # # Then ask for the minimum_enclosing_circle for each object named # in those indexes. MEC returns the i and j coordinate of the center # and the radius of the circle and that defines the circle entirely. # centers, radius = minimum_enclosing_circle(labels, indexes) 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 ]])) # # Multiply the Zernike polynomial by the image to dissect # the image by the Zernike basis set. # output_pixels = pixels * zernike_polynomial[:,:,0] # # The zernike polynomial is a complex number. We get a power # spectrum here to combine the real and imaginary parts # output_pixels = np.sqrt(output_pixels * output_pixels.conjugate()) # # 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 # result = fix(scind.sum(output_pixels.real, labels, indexes)) # # And we're done! Did you like it? Did you get it? # return result
def measure_zernike(self, pixels, labels, 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 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. # # You could move the calculation of the minimum enclosing circle # outside of this function. The function gets called more than # 10 times, so the same calculation is performed 10 times. # It would make the code a little more confusing, so I'm leaving # it as-is. ########################################### # # The minimum enclosing circle (MEC) is the smallest circle that # will fit around the object. We get the centers and radii of # all of the objects at once. You'll see how that lets us # compute the X and Y position of each pixel in a label all at # one go. # # First, get an array that lists the whole range of indexes in # the labels matrix. # indexes = np.arange(1, np.max(labels) + 1, dtype=np.int32) # # Then ask for the minimum_enclosing_circle for each object named # in those indexes. MEC returns the i and j coordinate of the center # and the radius of the circle and that defines the circle entirely. # centers, radius = minimum_enclosing_circle(labels, indexes) 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]])) # # Multiply the Zernike polynomial by the image to dissect # the image by the Zernike basis set. # output_pixels = pixels * zernike_polynomial[:, :, 0] # # The zernike polynomial is a complex number. We get a power # spectrum here to combine the real and imaginary parts # output_pixels = np.sqrt(output_pixels * output_pixels.conjugate()) # # 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 # result = fix(scind.sum(output_pixels.real, labels, indexes)) # # And we're done! Did you like it? Did you get it? # return result