Project

General

Profile

Bug #4855 ยป apBoxer.py

custom apBoxer.py for older myami installs - Neil Voss, 05/23/2017 04:18 PM

 
#!/usr/bin/env python
"""
This is a collection of function that
replace the EMAN1 batchboxer program with
added features like helical boxing at an
angle
"""

import sys
import math
import numpy
import random
from pyami import mrc
from scipy import ndimage #rotation function
from appionlib import apImagicFile #write imagic stacks
from appionlib import apDisplay
from appionlib.apImage import imagefilter #image clipping

##=================
def getBoxStartPosition(imgdata, halfbox, partdata, shiftdata):
### xcoord is the upper left area corner of the particle box
start_x = int(round( shiftdata['scale']*(partdata['xcoord'] - shiftdata['shiftx']) - halfbox ))
start_y = int(round( shiftdata['scale']*(partdata['ycoord'] - shiftdata['shifty']) - halfbox ))
return start_x,start_y

##=================
def checkBoxInImage(imgdims,start_x,start_y,boxsize):
return ( (start_x > 0 and start_x+boxsize <= imgdims['x'])
and (start_y > 0 and start_y+boxsize <= imgdims['y']) )

##=================
def processParticleData(imgdata, boxsize, partdatas, shiftdata, boxfile, rotate=False, checkInside=True):
"""
for a list of partdicts from database, apply shift
to get a new list with x, y, angle information

replaces writeParticlesToBoxfile()
"""
imgdims={}
imgdims['x'] = imgdata['image'].shape[1]
imgdims['y'] = imgdata['image'].shape[0]
#imgdims = imgdata['camera']['dimension']
if rotate is True:
### with rotate we use a bigger boxsize
halfbox = int(1.5*boxsize/2)
else:
halfbox = boxsize/2

parttree = []
boxedpartdatas = []
eliminated = 0
user = 0
noangle = 0

helixmode = False
for i in range(3):
partdata = random.choice(partdatas)
if partdata.get('helixnum', 0) > 0:
helixmode = True
break

### normal single particle
f = open(boxfile, 'w')
for i in range(len(partdatas)):
partdata = partdatas[i]

### require particle with rotation
if rotate is True and partdata['angle'] is None:
noangle += 1
continue

### xcoord is the upper left area corner of the particle box
start_x, start_y = getBoxStartPosition(None, halfbox, partdata, shiftdata)
if checkInside is False:
checkStatus = True
else:
checkStatus = checkBoxInImage(imgdims, start_x, start_y, boxsize)

if checkStatus is False:
eliminated += 1
continue

#write box information to file
if helixmode is True:
if i+1 >= len(partdatas):
continue
endhelix = partdatas[i+1]
if endhelix['helixnum'] != partdata['helixnum']:
continue
new_start_x, new_start_y = getBoxStartPosition(None, halfbox, endhelix, shiftdata)
if checkInside is False:
checkStatus = True
else:
checkStatus = checkBoxInImage(imgdims, new_start_x, new_start_y, boxsize)
if checkStatus is False:
eliminated += 1
continue
#write box information to file, helix mode
f.write("%d\t%d\t%d\t%d\t-1\n"%(start_x, start_y, boxsize, boxsize))
f.write("%d\t%d\t%d\t%d\t-2\n"%(new_start_x, new_start_y, boxsize, boxsize))
else:
#write box information to file, normal
f.write("%d\t%d\t%d\t%d\t-3\n"%(start_x, start_y, boxsize, boxsize))
partdict = {
'x_coord': start_x,
'y_coord': start_y,
'angle': partdata['angle'],
}
parttree.append(partdict)
boxedpartdatas.append(partdata)


f.close()

if eliminated > 0:
apDisplay.printMsg(str(eliminated)+" particle(s) eliminated because they were out of bounds")
if user > 0:
apDisplay.printMsg(str(user)+" particle(s) eliminated because they were 'user' labeled targets")
if noangle > 0:
apDisplay.printMsg(str(noangle)+" particle(s) eliminated because they had no rotation angle")

return parttree, boxedpartdatas

##=================
def getBoxBoundary(partdict, boxsize):
x1 = partdict['x_coord']
x2 = x1+boxsize
y1 = partdict['y_coord']
y2 = y1+boxsize
return x1,x2,y1,y2

##=================
def boxer(imgfile, parttree, outstack, boxsize, pixlimit=None):
"""
boxes the particles and saves them to a imagic file
"""
imgarray = mrc.read(imgfile)
imgarray = imagefilter.pixelLimitFilter(imgarray, pixlimit)
boxedparticles = boxerMemory(imgarray, parttree, boxsize)
apImagicFile.writeImagic(boxedparticles, outstack)
return True

##=================
def boxerMemory(imgarray, parttree, boxsize):
"""
boxes the particles and returns them as a list of numpy arrays
"""
boxedparticles = []
for partdict in parttree:
x1,x2,y1,y2 = getBoxBoundary(partdict, boxsize)
#numpy arrays are rows,cols --> y,x not x,y
#print x1,x2,y1,y2, imgarray.shape
boxpart = imgarray[y1:y2,x1:x2]
boxedparticles.append(boxpart)
return boxedparticles

##=================
def boxerRotate(imgfile, parttree, outstack, boxsize, pixlimit=None):
"""
boxes the particles with expanded size,
applies a rotation to particle,
reduces boxsize to requested size,
and saves them to a imagic file
"""
# size needed is sqrt(2)*boxsize, using 1.5 to be extra safe
bigboxsize = int(math.ceil(1.5*boxsize))
imgarray = mrc.read(imgfile)
imgarray = imagefilter.pixelLimitFilter(imgarray, pixlimit)
bigboxedparticles = boxerMemory(imgarray, parttree, bigboxsize)

boxedparticles = []
boxshape = (boxsize,boxsize)
apDisplay.printMsg("Rotating particles...")
for i in range(len(bigboxedparticles)):
if i % 10 == 0:
sys.stderr.write(".")
bigboxpart = bigboxedparticles[i]
partdict = parttree[i]
### add 90 degrees because database angle is from x-axis not y-axis
angle = partdict['angle']+90.0
rotatepart = ndimage.rotate(bigboxpart, angle=angle, reshape=False, order=1)
boxpart = imagefilter.frame_cut(rotatepart, boxshape)
boxedparticles.append(boxpart)
sys.stderr.write("done\n")
apImagicFile.writeImagic(boxedparticles, outstack)
return True

##=================
def boxMaskStack(bmstackf, partdatas, box, xmask, ymask, falloff, imask=None, norotate=False):
from appionlib.apSpider import operations
from appionlib import apEMAN
import os

# create blank image for mask using SPIDER
maskfile = "boxmask.spi"
operations.createBoxMask(maskfile,box,xmask,ymask,falloff,imask)

# convert mask to MRC
apEMAN.executeEmanCmd("proc2d boxmask.spi boxmask.mrc",verbose=False,showcmd=False)
os.remove("boxmask.spi")

maskarray = mrc.read("boxmask.mrc")

# box particles
maskedparts = []
for i in range(len(partdatas)):
if norotate is True:
rotatemask = maskarray
else:
angle = (-partdatas[i]['angle'])-90
rotatemask = ndimage.rotate(maskarray, angle=angle, reshape=False, order=1)
maskedparts.append(rotatemask)

# write to stack
apImagicFile.writeImagic(maskedparts, bmstackf)
os.remove("boxmask.mrc")
return bmstackf

##=================
def boxerFrameStack(framestackpath, parttree, outstack, boxsize,framelist):
"""
boxes the particles and returns them as a list of numpy arrays
"""
start_frame = framelist[0]
nframe = len(framelist)
apDisplay.printMsg("boxing %d particles from sum of total %d frames starting from frame %d using mmap" % (len(parttree),nframe,start_frame))
boxedparticles = []
stack = mrc.mmap(framestackpath)
for partdict in parttree:
x1,x2,y1,y2 = getBoxBoundary(partdict, boxsize)
apDisplay.printDebug(' crop range of (x,y)=(%d,%d) to (%d,%d)' % (x1,y1,x2-1,y2-1))
#numpy arrays are rows,cols --> y,x not x,y
boxpart = numpy.sum(stack[tuple(framelist),y1:y2,x1:x2],axis=0)
boxedparticles.append(boxpart)
apImagicFile.writeImagic(boxedparticles, outstack)
return True
    (1-1/1)