Skip to content

Commit d128d29

Browse files
authored
Merge pull request #5 from I2PC/olz_xmipp_base
Added XmippScript base class to implement Python programs
2 parents 9e9b1f3 + 56b1fb4 commit d128d29

3 files changed

Lines changed: 286 additions & 1 deletion

File tree

CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,9 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
4343

4444
# Add sources
4545
add_subdirectory(src)
46+
47+
# Install Python scripts
48+
install(DIRECTORY
49+
${CMAKE_CURRENT_SOURCE_DIR}/python/
50+
DESTINATION "${PYTHON_INSTALL_DIR}/"
51+
)

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.2
1+
0.2.0

python/xmipp_script.py

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
import re
2+
from collections import OrderedDict
3+
from distutils.spawn import find_executable
4+
5+
from xmippLib import *
6+
import os
7+
import sys
8+
import subprocess
9+
10+
def getXmippPath(*paths):
11+
""" Return the path of the Xmipp installation folder
12+
if a subfolder is provided, will be concatenated to the path
13+
"""
14+
15+
candidates = [] # First candidate from XMIPP_HOME, second from this file path
16+
envHome = os.environ.get('XMIPP_HOME', '') # the join do nothing if second is absolute
17+
candidates.append(os.path.join(os.environ.get('SCIPION_HOME', ''), envHome))
18+
# xmipp = build < bindings < python < xmipp_base.py
19+
candidates.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
20+
21+
for xmippHome in candidates:
22+
if (os.path.isfile(os.path.join(xmippHome, 'lib', 'libXmipp.so')) and
23+
os.path.isfile(os.path.join(xmippHome, 'bin', 'xmipp_mpi_reconstruct_significant'))):
24+
return os.path.join(xmippHome, *paths)
25+
26+
raise Exception("Error: Xmipp build directory not found. Searched at:\n - %s"
27+
% '\n - '.join(candidates))
28+
29+
def getXmippModel(*modelPath, **kwargs):
30+
""" Returns the path to the models folder followed by
31+
the given relative path.
32+
.../xmipp/models/myModel/myFile.h5 <= getModel('myModel', 'myFile.h5')
33+
34+
NOTE: it raise and exception when model not found, set doRaise=False
35+
in the arguments to skip that raise, especially in validation
36+
asserions!
37+
"""
38+
model = getXmippPath('models', *modelPath)
39+
# Raising an error to prevent posterior errors and to print a hint
40+
if kwargs.get('doRaise', True) and not os.path.exists(model):
41+
raise Exception("'%s' model not found. Please, run: \n"
42+
" > scipion installb deepLearningToolkit" % modelPath[0])
43+
return model
44+
45+
class XmippScript:
46+
""" This class will serve as wrapper around the XmippProgram class
47+
to have same facilities from Python scripts
48+
"""
49+
def __init__(self, runWithoutArgs=False):
50+
self._prog = Program(runWithoutArgs)
51+
52+
def defineParams(self):
53+
""" This function should be overwrited by subclasses for
54+
define its own parameters """
55+
pass
56+
57+
def readParams(self):
58+
""" This function should be overwrited by subclasses for
59+
and take desired params from command line """
60+
pass
61+
62+
def checkParam(self, param):
63+
return self._prog.checkParam(param)
64+
65+
def getParam(self, param, index=0):
66+
return self._prog.getParam(param, index)
67+
68+
def getIntParam(self, param, index=0):
69+
return int(self._prog.getParam(param, index))
70+
71+
def getDoubleParam(self, param, index=0):
72+
return float(self._prog.getParam(param, index))
73+
74+
def getListParam(self, param):
75+
return self._prog.getListParam(param)
76+
77+
def addUsageLine(self, line, verbatim=False):
78+
self._prog.addUsageLine(line, verbatim)
79+
80+
def addExampleLine(self, line, verbatim=True):
81+
self._prog.addExampleLine(line, verbatim)
82+
83+
def addParamsLine(self, line):
84+
self._prog.addParamsLine(line)
85+
86+
def run(self): # type: () -> object
87+
""" This function should be overwrited by subclasses and
88+
it the main body of the script """
89+
pass
90+
91+
def tryRun(self):
92+
""" This function should be overwrited by subclasses and
93+
it the main body of the script """
94+
try:
95+
print("WARNING: This is xmipp_base implementation for script")
96+
self.defineParams()
97+
doRun = self._prog.read(sys.argv)
98+
if doRun:
99+
self.readParams()
100+
self.run()
101+
return 0
102+
except Exception:
103+
import traceback
104+
traceback.print_exc(file=sys.stderr)
105+
return 1
106+
107+
@staticmethod
108+
def getModel(*modelPath, **kwargs):
109+
""" Returns the path to the models folder followed by
110+
the given relative path.
111+
.../xmipp/models/myModel/myFile.h5 <= getModel('myModel', 'myFile.h5')
112+
113+
NOTE: it raise and exception when model not found, set doRaise=False
114+
in the arguments to skip that raise, especially in validation
115+
asserions!
116+
"""
117+
return getXmippModel(*modelPath, **kwargs)
118+
119+
# FIXME: XmippMdRow is almost the same than pwem.emlib.metadata.utils.Row
120+
# FIXME: It's here because deep_denoising needs it and pwem can't be imported
121+
class XmippMdRow:
122+
""" Support Xmipp class to store label and value pairs corresponding to a
123+
Metadata row. - Code duplication alert!
124+
- Use this only outside of a Scipion plugin (i.e. in Xmipp programs).
125+
- For Scipion plugins (including Xmipp), use pwem.emlib.metadata.Row()
126+
"""
127+
def __init__(self):
128+
self._labelDict = OrderedDict() # Dictionary containing labels and values
129+
self._objId = None # Set this id when reading from a metadata
130+
131+
def getObjId(self):
132+
return self._objId
133+
134+
def hasLabel(self, label):
135+
return self.containsLabel(label)
136+
137+
def containsLabel(self, label):
138+
# Allow getValue using the label string
139+
if isinstance(label, str):
140+
label = str2Label(label)
141+
return label in self._labelDict
142+
143+
def removeLabel(self, label):
144+
if self.hasLabel(label):
145+
del self._labelDict[label]
146+
147+
def setValue(self, label, value):
148+
"""args: this list should contains tuples with
149+
MetaData Label and the desired value"""
150+
# Allow setValue using the label string
151+
if isinstance(label, str):
152+
label = str2Label(label)
153+
self._labelDict[label] = value
154+
155+
def getValue(self, label, default=None):
156+
""" Return the value of the row for a given label. """
157+
# Allow getValue using the label string
158+
if isinstance(label, str):
159+
label = str2Label(label)
160+
return self._labelDict.get(label, default)
161+
162+
def readFromMd(self, md, objId):
163+
""" Get all row values from a given id of a metadata. """
164+
self._labelDict.clear()
165+
self._objId = objId
166+
167+
for label in md.getActiveLabels():
168+
self._labelDict[label] = md.getValue(label, objId)
169+
170+
def addToMd(self, md):
171+
self.writeToMd(md, md.addObject())
172+
173+
def writeToMd(self, md, objId):
174+
""" Set back row values to a metadata row. """
175+
for label, value in self._labelDict.items():
176+
# TODO: Check how to handle correctly unicode type
177+
# in Xmipp and Scipion
178+
t = type(value)
179+
180+
if t is str:
181+
value = str(value)
182+
183+
if t is int and labelType(label) == LABEL_SIZET:
184+
value = int(value)
185+
186+
try:
187+
md.setValue(label, value, objId)
188+
except Exception as ex:
189+
print("XmippMdRow.writeToMd: Error writing value to metadata.",
190+
file=sys.stderr)
191+
print(" label: %s, value: %s, type(value): %s"
192+
% (label2Str(label), value, type(value)), file=sys.stderr)
193+
raise ex
194+
195+
def readFromFile(self, fn):
196+
md = MetaData(fn)
197+
self.readFromMd(md, md.firstObject())
198+
199+
def copyFromRow(self, other):
200+
for label, value in other._labelDict.items():
201+
self.setValue(label, value)
202+
203+
def __str__(self):
204+
s = '{'
205+
for k, v in self._labelDict.items():
206+
s += ' %s = %s\n' % (emlib.label2Str(k), v)
207+
return s + '}'
208+
209+
def __iter__(self):
210+
return self._labelDict.items()
211+
212+
def printDict(self):
213+
""" Fancy printing of the row, mainly for debugging. """
214+
print(str(self))
215+
216+
217+
def createMetaDataFromPattern(pattern, isStack=False, label="image"):
218+
''' Create a metadata from files matching pattern'''
219+
import glob
220+
if isinstance(pattern, list):
221+
files = []
222+
for pat in pattern:
223+
files += glob.glob(pat)
224+
else:
225+
files = glob.glob(pattern)
226+
files.sort()
227+
228+
label = str2Label(label) # Check for label value
229+
230+
mD = MetaData()
231+
inFile = FileName()
232+
233+
nSize = 1
234+
for file in files:
235+
fileAux=file
236+
if isStack:
237+
if file.endswith(".mrc"):
238+
fileAux=file+":mrcs"
239+
x, x, x, nSize = getImageSize(fileAux)
240+
if nSize != 1:
241+
counter = 1
242+
for jj in range(nSize):
243+
inFile.compose(counter, fileAux)
244+
objId = mD.addObject()
245+
mD.setValue(label, inFile, objId)
246+
mD.setValue(MDL_ENABLED, 1, objId)
247+
counter += 1
248+
else:
249+
objId = mD.addObject()
250+
mD.setValue(label, fileAux, objId)
251+
mD.setValue(MDL_ENABLED, 1, objId)
252+
return mD
253+
254+
255+
# TODO: All this MD functions can be implemented in core.metadata
256+
# but they are not, so far. Thus, they are here directly in python.
257+
def getMdSize(filename):
258+
""" Return the metadata size without parsing entirely. """
259+
md = MetaData()
260+
md.read(filename, 1)
261+
return md.getParsedLines()
262+
263+
264+
def isMdEmpty(filename):
265+
""" Use getMdSize to check if metadata is empty. """
266+
return getMdSize(filename) == 0
267+
268+
269+
def readInfoField(fnDir, block, label, xmdFile="iterInfo.xmd"):
270+
mdInfo = MetaData("%s@%s" % (block, os.path.join(fnDir, xmdFile)))
271+
return mdInfo.getValue(label, mdInfo.firstObject())
272+
273+
274+
def writeInfoField(fnDir, block, label, value, xmdFile="iterInfo.xmd"):
275+
mdInfo = MetaData()
276+
objId = mdInfo.addObject()
277+
mdInfo.setValue(label, value, objId)
278+
mdInfo.write("%s@%s" % (block, os.path.join(fnDir, xmdFile)), MD_APPEND)
279+

0 commit comments

Comments
 (0)