def draw_ellipse_to_file(jpgfile, imgarray, major, minor, angle, center=None, numpoints=64, color="#3d3df2", width=4): """ major - major axis radius (in pixels) minor - minor axis radius (in pixels) angle - angle (in degrees) center - position of centre of ellipse numpoints - # of points used that make an ellipse angle is positive toward y-axis """ if center is None: center = numpy.array(imgarray.shape, dtype=numpy.float)/2.0 points = ellipse.generate_ellipse(major, minor, angle, center, numpoints, None, "step", True) x = points[:,0] y = points[:,1] ## wrap around to end x = numpy.hstack((x, [x[0],])) y = numpy.hstack((y, [y[0],])) ## convert image originalimage = imagefile.arrayToImage(imgarray) originalimage = originalimage.convert("RGB") pilimage = originalimage.copy() draw = ImageDraw.Draw(pilimage) for i in range(len(x)-1): xy = (x[i], y[i], x[i+1], y[i+1]) draw.line(xy, fill=color, width=width) ## create an alpha blend effect originalimage = Image.blend(originalimage, pilimage, 0.9) originalimage.save(jpgfile, "JPEG", quality=85) return
def generateEllipseRangeMap2(ellipseParams, ellipseThresh, shape): ''' make an elliptical ring of width 1 based on ellipseParams ''' if ellipseParams is None: return None a = ellipseParams['a'] b = ellipseParams['b'] maxradius = max(a, b) if math.isnan(maxradius): return None numpoints = int(math.ceil(6.28 * maxradius)) center = numpy.array(shape, dtype=numpy.float64) / 2.0 ## ellip angle is positive toward y-axis alpha = ellipseParams['alpha'] points = ellipse.generate_ellipse(a, b, alpha, center=center, numpoints=numpoints, integers=True) #prepoints = ellipse.ellipsePoints(math.radians(1.0), center, a, b, alpha) #points = numpy.array(prepoints, dtype=numpy.int16) #remove negative coordinates, off screen if points.min() < 0: mins = points.min(1) abovezero = numpy.where(mins > 0) points = points[abovezero] #remove too large coordinates, off screen if points.max() > min(shape) - 1: maxs = points.max(1) belowedge = numpy.where(maxs < min(shape) - 1) points = points[belowedge] ellipseRange = numpy.zeros(shape, dtype=numpy.float64) ellipseRange[points[:, 1], points[:, 0]] = 1.0 ellipseRange = filters.maximum_filter(ellipseRange, size=ellipseThresh) ellipseRange2 = filters.maximum_filter(ellipseRange, size=ellipseThresh * 2) ellipseRange = ellipseRange + ellipseRange2 * 0.1 return ellipseRange
def generateEllipseRangeMap2(ellipseParams, ellipseThresh, shape): ''' make an elliptical ring of width 1 based on ellipseParams ''' if ellipseParams is None: return None a = ellipseParams['a'] b = ellipseParams['b'] maxradius = max(a,b) if math.isnan(maxradius): return None numpoints = int(math.ceil(6.28*maxradius)) center = numpy.array(shape, dtype=numpy.float64)/2.0 ## ellip angle is positive toward y-axis alpha = ellipseParams['alpha'] points = ellipse.generate_ellipse(a, b, alpha, center=center, numpoints=numpoints, integers=True) #prepoints = ellipse.ellipsePoints(math.radians(1.0), center, a, b, alpha) #points = numpy.array(prepoints, dtype=numpy.int16) #remove negative coordinates, off screen if points.min() < 0: mins = points.min(1) abovezero = numpy.where(mins > 0) points = points[abovezero] #remove too large coordinates, off screen if points.max() > min(shape)-1: maxs = points.max(1) belowedge = numpy.where(maxs < min(shape)-1) points = points[belowedge] ellipseRange = numpy.zeros(shape, dtype=numpy.float64) ellipseRange[points[:,1],points[:,0]] = 1.0 ellipseRange = filters.maximum_filter(ellipseRange, size=ellipseThresh) ellipseRange2 = filters.maximum_filter(ellipseRange, size=ellipseThresh*2) ellipseRange = ellipseRange + ellipseRange2*0.1 return ellipseRange
def findAstigmatism(fftarray, freq, defocus, resolution, ctfvalues, peakNum=1): """ find the astigmatism from a radial 1D estimate of the defocus """ #searchError = resolution/100. extrema = ctftools.getCtfExtrema(defocus, freq*1e10, ctfvalues['cs'], ctfvalues['volts'], ctfvalues['amplitude_contrast'], numzeros=peakNum*2+1, zerotype="all") if len(extrema) < 2*peakNum: return None minEdgeRadius = int(math.ceil(extrema[peakNum-1])) #first peak maxEdgeRadius = int(math.ceil(extrema[peakNum])) #second peak newshape = (maxEdgeRadius*2, maxEdgeRadius*2) print "newshape",newshape fftcenter = copy.deepcopy(imagefilter.frame_cut(fftarray, newshape)) showImage(fftcenter) shape = fftcenter.shape ## create a grid of distance from the center xhalfshape = shape[0]/2.0 x = numpy.arange(-xhalfshape, xhalfshape, 1) + 0.5 yhalfshape = shape[1]/2.0 y = numpy.arange(-yhalfshape, yhalfshape, 1) + 0.5 xx, yy = numpy.meshgrid(x, y) radialArray = xx**2 + yy**2 - 0.5 #radialArray = numpy.sqrt(radial) maxVal = fftcenter.max()*2 minVal = fftcenter.min() if debug is True: fftcenter = numpy.where(radialArray > maxEdgeRadius**2, maxVal, fftcenter) showImage(fftcenter) fftcenter = numpy.where(radialArray < minEdgeRadius**2, maxVal, fftcenter) showImage(fftcenter) angleArray = numpy.arctan2(-yy,xx) numSteps = 360 angleArray += math.pi angleArray /= 2*math.pi angleArray *= numSteps angleArray = numpy.asarray(angleArray, dtype=numpy.uint16) showImage(angleArray) dataIntegers = numpy.array(range(numSteps)) xyData = numpy.array( scipy.ndimage.measurements.minimum_position( fftcenter, angleArray, dataIntegers)) if debug is True: fftcenter[xyData[:,0], xyData[:,1]] = maxVal*3 showImage(fftcenter) ellipseParams = ellipse.totalLeastSquareEllipse(xyData, center=(xhalfshape, yhalfshape)) if ellipseParams is None: return None ellipse.printParamsDict(ellipseParams) if debug is True: numPoints = int(math.pi*(ellipseParams['a']+ellipseParams['b'])) ellPoints = ellipse.generate_ellipse(ellipseParams['a'], ellipseParams['b'], ellipseParams['alpha'], center=ellipseParams['center'], numpoints=numPoints, integers=True) fftcenter[ellPoints[:,0], ellPoints[:,1]] += maxVal showImage(fftcenter) return ellipseParams
def drawPowerSpecImage(self, origpowerspec, maxsize=1200): origpowerspec = ctftools.trimPowerSpectraToOuterResolution(origpowerspec, self.plotlimit2DAngstrom, self.trimfreq) if self.debug is True: print "origpowerspec shape", origpowerspec.shape #compute elliptical average and merge with original image pixelrdata, rotdata = ctftools.ellipticalAverage(origpowerspec, self.ellipratio, self.angle, self.ringwidth*3, 1, full=True) ellipavgpowerspec = ctftools.unEllipticalAverage(pixelrdata, rotdata, self.ellipratio, self.angle, origpowerspec.shape) halfshape = origpowerspec.shape[1]/2 halfpowerspec = numpy.hstack( (origpowerspec[:,:halfshape] , ellipavgpowerspec[:,halfshape:] ) ) if halfpowerspec.shape != origpowerspec.shape: apDisplay.printError("Error in power spectra creation") if max(halfpowerspec.shape) > maxsize: scale = maxsize/float(max(halfpowerspec.shape)) #scale = math.sqrt((random.random()+random.random()+random.random())/3.0) apDisplay.printMsg( "Scaling final powerspec image by %.3f"%(scale)) powerspec = imagefilter.scaleImage(halfpowerspec, scale) else: scale = 1280./float(max(halfpowerspec.shape)) powerspec = imagefilter.scaleImage(halfpowerspec, scale) #scale = 1.0 #powerspec = halfpowerspec.copy() self.scaleapix = self.trimapix self.scalefreq = self.trimfreq/scale if self.debug is True: print "orig pixel", self.apix print "trim pixel", self.trimapix print "scale pixel", self.scaleapix numzeros = 13 radii1 = ctftools.getCtfExtrema(self.defocus1, self.scalefreq*1e10, self.cs, self.volts, self.ampcontrast, numzeros=numzeros, zerotype="valley") radii2 = ctftools.getCtfExtrema(self.defocus2, self.scalefreq*1e10, self.cs, self.volts, self.ampcontrast, numzeros=numzeros, zerotype="valley") #smallest of two defocii firstpeak = radii2[0] ### ### PART 9: DRAW THE 2D POWERSPEC IMAGE ### center = numpy.array(powerspec.shape, dtype=numpy.float)/2.0 foundzeros = min(len(radii1), len(radii2)) """ pyplot.clf() ax = pyplot.subplot(1,1,1) pyplot.xticks([], []) pyplot.yticks([], []) pyplot.imshow(powerspec) pyplot.gray() for i in range(foundzeros): # because |def1| < |def2| ==> firstzero1 > firstzero2 major = radii1[i]*2 minor = radii2[i]*2 ell = Ellipse(xy=center, width=major, height=minor, angle=self.angle+90, fill=False, edgecolor="yellow", antialiased=True, linewidth=0.5) ax.add_artist(ell) pyplot.subplots_adjust(wspace=0, hspace=0, bottom=0, left=0, top=1, right=1, ) self.newpowerspecfile = apDisplay.short(self.imgname)+"-powerspec-new.png" pyplot.savefig(self.newpowerspecfile, format="png", dpi=150, pad_inches=0.0) """ ### ### PART 9: DRAW THE 2D POWERSPEC IMAGE ### apDisplay.printColor("PART 9: DRAW THE 2D POWERSPEC IMAGE", "magenta") center = numpy.array(powerspec.shape, dtype=numpy.float)/2.0 originalimage = imagefile.arrayToImage(powerspec) originalimage = originalimage.convert("RGB") pilimage = originalimage.copy() draw = ImageDraw.Draw(pilimage) ######### ## draw astig axis line, if astig > 5% ######### perdiff = 2*abs(self.defocus1-self.defocus2)/abs(self.defocus1+self.defocus2) if self.debug is True: print "Percent Difference %.1f"%(perdiff*100) if perdiff > 0.05: #print self.angle, radii2[0], center x = 1*firstpeak*math.cos(math.radians(self.angle)) y = firstpeak*math.sin(math.radians(self.angle)) #print x,y xy = (x+center[0], y+center[1], -x+center[0], -y+center[1]) #print xy draw.line(xy, fill="#f23d3d", width=10) elif perdiff > 1e-6: #print self.angle, radii2[0], center x = 1*firstpeak*math.cos(math.radians(self.angle)) y = firstpeak*math.sin(math.radians(self.angle)) #print x,y xy = (x+center[0], y+center[1], -x+center[0], -y+center[1]) #print xy draw.line(xy, fill="#f23d3d", width=2) ######### ## draw colored CTF Thon rings ######### foundzeros = min(len(radii1), len(radii2)) #color="#3d3dd2" #blue color="#ffd700" #gold for i in range(foundzeros): # because |def1| < |def2| ==> firstzero1 > firstzero2 major = radii1[i] minor = radii2[i] if self.debug is True: print "major=%.1f, minor=%.1f, angle=%.1f"%(major, minor, self.angle) if minor > powerspec.shape[0]/math.sqrt(3): # this limits how far we draw out the ellipses sqrt(3) to corner, just 2 inside line break width = int(math.ceil(math.sqrt(numzeros - i)))*2 ### determine color of circle currentres = 1.0/(major*self.scalefreq) if currentres > self.res80: ringcolor = "green" elif currentres > self.res50: ringcolor = "gold" else: ringcolor = "red" ### determine number of points to use to draw ellipse, minimize distance btw points #isoceles triangle, b: radius ot CTF ring, a: distance btw points #a = 2 * b sin (theta/2) #a / 2b = sin(theta/2) #theta = 2 * asin (a/2b) #numpoints = 2 pi / theta ## define a to be 5 pixels a = 40 theta = 2.0 * math.asin (a/(2.0*major)) skipfactor = 2 numpoints = int(math.ceil(2.0*math.pi/theta/skipfactor))*skipfactor + 1 #print "numpoints", numpoints points = ellipse.generate_ellipse(major, minor, math.radians(self.angle), center, numpoints, None, "step", True) x = points[:,0] y = points[:,1] ## wrap around to end x = numpy.hstack((x, [x[0],])) y = numpy.hstack((y, [y[0],])) ## convert image numsteps = int(math.floor((len(x)-2)/skipfactor)) for j in range(numsteps): k = j*skipfactor xy = (x[k], y[k], x[k+1], y[k+1]) draw.line(xy, fill=ringcolor, width=width) ######### ## draw blue resolution ring ######### # 1/res = freq * pixrad => pixrad = 1/(res*freq) maxrad = (max(powerspec.shape)-1)/2.0 - 3 maxres = 1.0/(self.scalefreq*maxrad) bestres = math.ceil(maxres) pixrad = 1.0/(self.scalefreq*bestres) if self.debug is True: print "bestres %d Angstroms (max: %.3f)"%(bestres, maxres) print "pixrad %d (max: %.3f)"%(pixrad, maxrad) if pixrad > maxrad: apDisplay.printError("Too big of outer radius to draw") outpixrad = math.ceil(pixrad)+1 inpixrad = math.floor(pixrad)-1 for i in numpy.arange(-4.0,4.01,0.01): r = pixrad + i blackxy = numpy.array((center[0]-r,center[1]-r, center[0]+r,center[1]+r), dtype=numpy.float64) draw.ellipse(tuple(blackxy), outline="black") for i in numpy.arange(-1.50,1.51,0.01): r = pixrad + i whitexy = numpy.array((center[0]-r,center[1]-r, center[0]+r,center[1]+r), dtype=numpy.float64) draw.ellipse(tuple(whitexy), outline="#0BB5FF") ######### ## setup font to add text ######### fontpath = "/usr/share/fonts/liberation/LiberationSans-Regular.ttf" from PIL import ImageFont if os.path.isfile(fontpath): fontsize = int(math.ceil( 48/2. * min(powerspec.shape)/float(maxsize))*2) font = ImageFont.truetype(fontpath, fontsize) else: font = ImageFont.load_default() ######### ## add resolution ring text ######### angrad = maxrad/math.sqrt(2) + 1 coord = (angrad+maxrad, angrad+maxrad) for i in [-2,2]: for j in [-2,2]: draw.text((coord[0]+i,coord[1]+j), "%.1f A"%(bestres), font=font, fill="black") draw.text(coord, "%.1f A"%(bestres), font=font, fill="#0BB5FF") ######### ## add defocus value text ######### meandef = abs(self.defocus1+self.defocus2)/2.0 deftext = "%.2f um"%(meandef*1e6) tsize = draw.textsize(deftext, font=font) coord = (powerspec.shape[0]-4-tsize[0], powerspec.shape[0]-4-tsize[1]) for i in [-2,2]: for j in [-2,2]: draw.text((coord[0]+i,coord[1]+j), deftext, font=font, fill="black") draw.text(coord, deftext, font=font, fill="#AB82FF") ######### ## add text about what sides of powerspec are: ## left - raw data; right - elliptical average data ######### leftcoord = (4, 4) for i in [-3, -1, 0, 1, 3]: for j in [-3, -1, 0, 1, 3]: draw.text((leftcoord[0]+i,leftcoord[1]+j) , "Raw CTF Data", font=font, fill="black") draw.text(leftcoord, "Raw CTF Data", font=font, fill="#00BFFF") tsize = draw.textsize("Elliptical Average", font=font) xdist = powerspec.shape[0] - 4 - tsize[0] rightcoord = (xdist, 4) for i in [-2,2]: for j in [-2,2]: draw.text((rightcoord[0]+i,rightcoord[1]+j), "Elliptical Average", font=font, fill="black") draw.text(rightcoord, "Elliptical Average", font=font, fill="#00BFFF") ######### ## create an alpha blend effect ######### originalimage = Image.blend(originalimage, pilimage, 0.95) apDisplay.printMsg("Saving 2D powerspectra to file: %s"%(self.powerspecfile)) #pilimage.save(self.powerspecfile, "JPEG", quality=85) originalimage.save(self.powerspecfile, "JPEG", quality=85) if not os.path.isfile(self.powerspecfile): apDisplay.printWarning("power spec file not created") if self.debug is True: #powerspecjpg = Image.open(self.powerspecfile) #powerspecjpg.show() pass return