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 ellipseRANSAC(edgeMap, ellipseThresh=2, minPercentGoodPoints=0.001, certainProb=0.9, maxiter=10000, maxRatio=4.0): """ takes 2D edge map from image and trys to find a good ellipse in the data """ shrinkEdgeMap = numpy.float64(trimZeroEdges(edgeMap)) #shrinkEdgeMap = numpy.float64(edgeMap) shape = shrinkEdgeMap.shape print "RANSAC shape", shape ## make a list of edges, with x, y radii bottomEdgeMap = numpy.copy(shrinkEdgeMap) bottomEdgeMap[:shape[0]/2,:] = 0 topEdgeMap = numpy.copy(shrinkEdgeMap) topEdgeMap[shape[0]/2:,:] = 0 rightEdgeMap = numpy.copy(shrinkEdgeMap) rightEdgeMap[:,:shape[1]/2] = 0 leftEdgeMap = numpy.copy(shrinkEdgeMap) leftEdgeMap[:,shape[1]/2:] = 0 #sort edges into quadrants and choose that way center = numpy.array(shape, dtype=numpy.float64)/2.0 - 0.5 edgeList = numpy.array(numpy.where(shrinkEdgeMap), dtype=numpy.float64).transpose() - center bottomEdgeList = numpy.array(numpy.where(bottomEdgeMap), dtype=numpy.float64).transpose() - center topEdgeList = numpy.array(numpy.where(topEdgeMap), dtype=numpy.float64).transpose() - center rightEdgeList = numpy.array(numpy.where(rightEdgeMap), dtype=numpy.float64).transpose() - center leftEdgeList = numpy.array(numpy.where(leftEdgeMap), dtype=numpy.float64).transpose() - center numEdges = shrinkEdgeMap.sum() if numEdges != len(edgeList): print "something weird in array sizes" return None numSamples = 4 ## count rejects areaReject = 0 distReject = 0 ratioReject = 0 sizeReject = 0 validEllipse = 0 ## reject criteria maxDistFromCenter = math.hypot(shape[0], shape[1])/10.0 squareArea = max(edgeMap.shape)**2/4 #quarter of the image, little less than indcribed circle maxShrinkRadius = int(max(shape)*0.7) #to the corner of shrink image maxNormRadius = max(edgeMap.shape)/2 #to the side of full image maxRadius = min(maxShrinkRadius, maxNormRadius) minGoodPoints = minPercentGoodPoints*numEdges finalMinEdges = numEdges*(1.0 - math.exp( math.log(1.0-certainProb)/maxiter ) )**(1.0/numSamples) mostGoodPoints = 0 bestEllipseParams = None # {'center':(x,y), 'a':a, 'b':b, 'alpha':radians} iternum = 0 t0 = time.time() while iternum <= maxiter: iternum += 1 currentProb = 1.0 - math.exp( math.log(1.0-certainProb)/iternum ) currentProb = currentProb**(1.0/numSamples) if iternum % 500 == 0: print ("RANSAC iter=%d/%d, points=%d, need=%d->%d, bestProb=%.1f, timePer=%.4f" %(iternum, maxiter, mostGoodPoints, currentProb*numEdges, finalMinEdges, mostGoodPoints/float(numEdges)*100, (time.time()-t0)/float(iternum))) ## check to see if we can stop if mostGoodPoints > currentProb*numEdges: #currentProb = mostGoodPoints/float(numEdges) #print "currentProb", currentProb #successProb = 1.0 - math.exp( iternum * math.log(1.0 - currentProb**numSamples) ) print "\nRANSAC SUCCESS" break if currentProb < minPercentGoodPoints: print "\nRANSAC FAILURE" break if iternum >= maxiter: print "\nRANSAC GAVE UP" break #choose random edges #might be better to sort edges into quadrants and choose that way currentEdges = [] if len(bottomEdgeList) > 0: currentEdges.append(random.choice(bottomEdgeList)) if len(topEdgeList) > 0: currentEdges.append(random.choice(topEdgeList)) if len(leftEdgeList) > 0: currentEdges.append(random.choice(leftEdgeList)) if len(rightEdgeList) > 0: currentEdges.append(random.choice(rightEdgeList)) ##NEIL IDEA: could select a random edge instead of an edge point??? if len(currentEdges) < 3: print "error not enough edges" break currentEdges = numpy.array(currentEdges, dtype=numpy.int16) ## convert rows, columns into x, y by swapping columns convertEdges = currentEdges.copy() convertEdges[:,[0, 1]] = currentEdges[:,[1, 0]] convertEdges = numpy.vstack( (currentEdges[:,1], currentEdges[:,0]) ).T # solve centered ellipse, fixed center centeredParams = ellipse.solveEllipseOLS(convertEdges) #print centeredParams if centeredParams is None: #sys.stderr.write("c") continue ## check to see if ellipse has a area smaller than circle inscribed in image area = centeredParams['a'] * centeredParams['b'] if area > squareArea: areaReject += 1 #sys.stderr.write("A%d "%(areaReject)) continue radius = max(centeredParams['a'],centeredParams['b']) if radius > maxRadius: sizeReject += 1 #sys.stderr.write("S%d "%(sizeReject)) continue ratio = centeredParams['a']/float(centeredParams['b']) if ratio > maxRatio or ratio < 1.0/maxRatio: ratioReject += 1 #sys.stderr.write("R%d "%(ratioReject)) continue ## check to see if ellipse has a circumference smaller than something # solve general ellipse, floating center ## only a few points, can use solveEllipseGander fit #generalParams = ellipse.solveEllipseB2AC(convertEdges) generalParams = ellipse.solveEllipseGander(convertEdges) if generalParams is not None: ## check center to see if its reasonably close to center distFromCenter = math.hypot(generalParams['center'][0], generalParams['center'][1]) if distFromCenter > maxDistFromCenter: distReject += 1 #sys.stderr.write("D%d "%(distReject)) continue ## check to see if ellipse has a area smaller than circle inscribed in image area = math.pi * centeredParams['a'] * centeredParams['b'] if area > squareArea: areaReject += 1 #sys.stderr.write("a%d "%(areaReject)) continue radius = max(centeredParams['a'],centeredParams['b']) if radius > maxRadius: sizeReject += 1 #sys.stderr.write("s%d "%(sizeReject)) continue ratio = centeredParams['a']/float(centeredParams['b']) if ratio > maxRatio or ratio < 1.0/maxRatio: ## should be a > b ratioReject += 1 #sys.stderr.write("r%d "%(ratioReject)) continue ## create an outline of the ellipse ellipseMap = generateEllipseRangeMap2(centeredParams, ellipseThresh, shape) if ellipseMap is None: continue ## take overlap of edges and fit area to determine number of good points goodPoints = (shrinkEdgeMap*ellipseMap).sum() #if goodPoints > mostGoodPoints or iternum%100 == 0: # print ("\ngood points=%d (best=%d; need=%d->%d, bestProb=%.1f, iter=%d/%d, timePer=%.4f)" # %(goodPoints, mostGoodPoints, currentProb*numEdges, finalMinEdges, # mostGoodPoints/float(numEdges)*100, iternum, maxiter, # (time.time()-t0)/float(iternum))) validEllipse += 1 if goodPoints < minGoodPoints or goodPoints < mostGoodPoints: #sys.stderr.write(".") continue if goodPoints > mostGoodPoints: ### refine the ellipse parameters to see if we can do better centeredEllipseMap1 = generateEllipseRangeMap2(centeredParams, ellipseThresh, shape) centeredEllipseList1 = numpy.array(numpy.where(centeredEllipseMap1), dtype=numpy.float64).transpose() - center betterEllipseParams1 = ellipse.totalLeastSquareEllipse(centeredEllipseList1) betterEllipseMap1 = generateEllipseRangeMap2(betterEllipseParams1, ellipseThresh, shape) betterGoodPoints1 = (shrinkEdgeMap*betterEllipseMap1).sum() centeredEllipseMap2 = generateEllipseRangeMap2(centeredParams, ellipseThresh*8, shape) centeredEllipseList2 = numpy.array(numpy.where(centeredEllipseMap2), dtype=numpy.float64).transpose() - center betterEllipseParams2 = ellipse.totalLeastSquareEllipse(centeredEllipseList2) betterEllipseMap2 = generateEllipseRangeMap2(betterEllipseParams2, ellipseThresh, shape) betterGoodPoints2 = (shrinkEdgeMap*betterEllipseMap2).sum() #print goodPoints, betterGoodPoints1, betterGoodPoints2 if betterGoodPoints1 > goodPoints and betterGoodPoints1 > betterGoodPoints2: bestEllipseParams = betterEllipseParams1 mostGoodPoints = betterGoodPoints1 elif betterGoodPoints2 > goodPoints: bestEllipseParams = betterEllipseParams2 mostGoodPoints = betterGoodPoints2 else: bestEllipseParams = centeredParams mostGoodPoints = goodPoints printParams(bestEllipseParams) #if iternum > 100: # imagefile.arrayToJpeg(centeredEllipseMap1, "map%05d.jpg"%(iternum)) ### end loop and do stuff #bestEllipseMap = generateEllipseRangeMap2(bestEllipseParams, ellipseThresh*1.5, edgeMap.shape) #bestEllipseList = numpy.array(numpy.where(bestEllipseMap), dtype=numpy.float64).transpose() - center #betterEllipseParams = ellipse.solveEllipseOLS(bestEllipseList) print "total iterations", iternum print "\tpercent good points %.1f"%(mostGoodPoints/float(numEdges)*100) print "\ttime per iter %.2f ms"%(1000.*(time.time()-t0)/float(iternum)) print "\tvalid ellipses", validEllipse print "\toff-center rejects", distReject print "\tlarge area rejects", areaReject print "\tlarge radius rejects", sizeReject print "\toblong ratio rejects", ratioReject if bestEllipseParams is not None: printParams(bestEllipseParams) else: print "Fit failed" return None return bestEllipseParams