Skip to content

Commit 437e334

Browse files
chkim-usgsKelvinrr
andauthored
findFeaturesSegment.py script fixes (#5565)
* Fix path issue, reduce redundant overlaps * Added comment * Remove old image_sets * Add check for match_segment_n * Revert overlap changes * updated script to improve times and fix bugs * added workdir param * made sure file are cleaned up when process fails * removed bug * fixes to tolist * Trigger build --------- Co-authored-by: Kelvin <[email protected]>
1 parent 2ead6f7 commit 437e334

File tree

2 files changed

+155
-82
lines changed

2 files changed

+155
-82
lines changed

isis/python_bindings/apps/findFeaturesSegment/findFeaturesSegment.py

+141-81
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,21 @@ def read_cubelist(cube_list : Path):
6767
list
6868
list of files
6969
"""
70+
files = []
71+
7072
with open(cube_list) as c:
7173
content = c.read()
7274
content = content.strip()
7375
files = content.split("\n")
74-
return files
76+
77+
for i in range(len(files)):
78+
files[i] = os.path.abspath(files[i])
79+
return files
80+
7581

7682

77-
def segment(img_path : Path, nlines : int = MAX_LEN):
83+
84+
def segment(img_path : Path, workdir : Path, nlines : int = MAX_LEN):
7885
"""
7986
Segments an image into multiple parts.
8087
@@ -97,19 +104,28 @@ def segment(img_path : Path, nlines : int = MAX_LEN):
97104

98105
segment_metadata = {}
99106
try:
100-
ret = kisis.segment(img_path, nl=nlines, overlap=0, pref__="$ISISROOT/IsisPreferences")
107+
# change workdir so output goes there
108+
oldpwd = os.getcwd()
109+
110+
# create if it doesn't exist
111+
workdir.mkdir(parents=True, exist_ok=True)
112+
work_img = workdir / img_path.name
113+
shutil.copyfile(img_path, work_img)
114+
ret = kisis.segment(work_img, nl=nlines, overlap=0, pref__="$ISISROOT/IsisPreferences")
115+
os.remove(work_img)
116+
101117
log.debug(f"{ret}")
102118
segment_metadata = pvl.loads(filter_progress(ret.stdout))
103119

104120
# comes nested in a "results" group, trim it off
105121
segment_metadata = [s[1] for s in segment_metadata]
106122

107-
glob_str = str(img_path.parent / img_path.stem) + ".segment*"
123+
glob_str = str(workdir / img_path.stem) + ".segment*"
108124
log.debug(f"globbing segments: glob({glob_str})")
109125
segments = sorted(glob(glob_str))
110-
126+
111127
log.debug(f"segments: {segments}")
112-
128+
113129
i = 0
114130
for s, meta in zip(segments, segment_metadata):
115131
i += 1
@@ -133,19 +149,27 @@ def segment(img_path : Path, nlines : int = MAX_LEN):
133149

134150

135151
def generate_cnet(params, images):
136-
137-
match_segment_n = images["match"]["Segment"]
152+
if isinstance(images["match"], list):
153+
images_match_n = images["match"][0]
154+
else:
155+
images_match_n = images["match"]
156+
match_segment_n = images_match_n["Segment"]
138157
from_segment_n = images["from"][0]["Segment"]
139158

159+
print(images)
160+
workdir = Path(params["WORKDIR"])
161+
140162
new_params = deepcopy(params)
141163
new_params.pop("NL")
142164
new_params.pop("MINAREA")
143165
new_params.pop("MINTHICKNESS")
144-
166+
new_params.pop("WORKDIR")
167+
168+
145169
# make sure none of these keys are still in the params
146170
new_params.pop("FROMLIST", None)
147171
new_params.pop("FROM", None)
148-
new_params["MATCH"] = images["match"]["Path"]
172+
new_params["MATCH"] = images_match_n["Path"]
149173

150174
og_onet = Path(params["ONET"])
151175
og_tolist = Path(params["TOLIST"])
@@ -158,7 +182,7 @@ def generate_cnet(params, images):
158182

159183
match_stem = Path(new_params["MATCH"]).stem
160184

161-
fromlist_path = Path(og_tolist.parent / f"from_images_segment{from_segment_n}.lis")
185+
fromlist_path = Path(workdir / f"from_images_segment{from_segment_n}.lis")
162186
from_stem = fromlist_path.stem
163187

164188
if "debuglog" in new_params:
@@ -168,49 +192,40 @@ def generate_cnet(params, images):
168192

169193
new_params["NETWORKID"] = og_networkid + f"_{match_segment_n}_{from_stem}"
170194
new_params["POINTID"] = og_pointid + f"_{match_segment_n}_{from_stem}"
171-
new_params["ONET"] = f"{og_onet.parent/og_onet.stem}_{match_segment_n}_{from_stem}.net"
172-
new_params["TOLIST"] = f"{og_tolist.parent/og_tolist.stem}_{match_segment_n}_{from_stem}.lis"
173-
195+
new_params["ONET"] = f"{og_onet.stem}_{match_segment_n}_{from_stem}.net"
196+
new_params["TOLIST"] = f"{og_tolist.stem}_{match_segment_n}_{from_stem}.lis"
197+
174198
log.debug(new_params)
175199

200+
overlapfromlist = workdir / f"{og_tolist.stem}_{match_segment_n}_{from_stem}_overlap_fromlist.lis"
201+
overlaptolist = workdir / f"{og_tolist.stem}_{match_segment_n}_{from_stem}.overlaps"
202+
176203
# check for overlaps
177204
is_overlapping = []
178-
for image in from_images:
179-
with tempfile.TemporaryDirectory() as tmpdir:
180-
tmpdir = Path(tmpdir)
181-
overlapfromlist = tmpdir / "fromlist.lis"
182-
overlaptolist = tmpdir / "tolist.lis"
183-
184-
kisis.fromlist.make([*from_images, new_params["MATCH"]], overlapfromlist)
185-
186-
try:
187-
kisis.findimageoverlaps(fromlist=overlapfromlist, overlaplist=overlaptolist)
188-
except subprocess.CalledProcessError as err:
189-
print('Had an ISIS error:')
190-
print(' '.join(err.cmd))
191-
print(err.stdout)
192-
print(err.stderr)
193-
raise err
194-
195-
ret = kisis.overlapstats(fromlist=overlapfromlist, overlaplist=overlaptolist, pref__="$ISISROOT/IsisPreferences")
196-
stats = pvl.loads(filter_progress(ret.stdout))
197-
log.debug(f"overlap stats: {ret.stdout}")
198-
199-
# first, throw it out if there is no overlap whatsoever
200-
is_pair_overlapping = not any([k[1].get("NoOverlap", "") == new_params["MATCH"] for k in stats])
201-
202-
if is_pair_overlapping:
203-
is_thick_enough = stats["Results"]["ThicknessMinimum"] > float(params.get("MINTHICKNESS", 0))
204-
is_area_large_enough = stats["Results"]["AreaMinimum"] > float(params.get("MINAREA", 0))
205-
is_pair_overlapping = all([is_thick_enough, is_area_large_enough])
206-
is_overlapping.append(is_pair_overlapping)
207-
else: # not overlapping
208-
is_overlapping.append(False)
209-
210-
# mask images
211-
from_images = list(compress(from_images, is_overlapping))
212-
log.debug(f"From images overlapping Match: {from_images}")
205+
all_images = [*from_images, new_params["MATCH"]]
206+
print("ALL IMAGES: ", " ".join(all_images))
207+
kisis.fromlist.make(all_images, overlapfromlist)
213208

209+
try:
210+
kisis.findimageoverlaps(fromlist=overlapfromlist, overlaplist=overlaptolist)
211+
except subprocess.CalledProcessError as err:
212+
print('Find Image Overlaps Had an ISIS error:')
213+
print(' '.join(err.cmd))
214+
print(err.stdout)
215+
print(err.stderr)
216+
return "No Overlaps From FindImageOverlaps"
217+
218+
ret = kisis.overlapstats(fromlist=overlapfromlist, overlaplist=overlaptolist, pref__="$ISISROOT/IsisPreferences")
219+
stats = pvl.loads(filter_progress(ret.stdout))
220+
log.debug(f"overlap stats: {ret.stdout}")
221+
222+
# # first, throw it out if there is no overlap whatsoever
223+
# has_overlap = not any([len(k[1].get("NoOverlap", "")) == new_params["MATCH"] for k in stats])
224+
225+
# # mask images
226+
# from_images = list(compress(from_images, is_overlapping))
227+
log.debug(f"From images overlapping Match: {from_images}")
228+
log.debug(f"Has overlaps list: {is_overlapping}")
214229

215230
if from_images:
216231
log.debug(f"FROMLIST: {from_images}")
@@ -221,32 +236,35 @@ def generate_cnet(params, images):
221236
else:
222237
log.debug(f"{fromlist_path} already exists")
223238
new_params["FROMLIST"] = str(fromlist_path)
239+
240+
log.debug(f"Running FindFeatures with Params: {new_params}")
224241

225242
try:
226243
ret = kisis.findfeatures(**new_params)
227244
log.debug(f"returned: {ret}")
228245
except subprocess.CalledProcessError as err:
229-
log.debug('Had an ISIS error:')
246+
log.debug('Find Features Had an ISIS error:')
230247
log.debug(' '.join(err.cmd))
231248
log.debug(err.stdout)
232249
log.debug(err.stderr)
233-
return "ERROR"
234250

235-
segmented_net = cnet.from_isis(new_params["ONET"])
251+
if os.path.exists(new_params["ONET"]):
252+
segmented_net = cnet.from_isis(new_params["ONET"])
236253

237-
# starting sample in inclusive, so subtract 1
238-
segmented_net.loc[segmented_net.serialnumber == images["match"]["SN"], "line"] += images["match"]["StartingLine"]-1
254+
# starting sample in inclusive, so subtract 1
255+
segmented_net.loc[segmented_net.serialnumber == images_match_n["SN"], "line"] += images_match_n["StartingLine"]-1
239256

240-
# offset the images
241-
for k, image in enumerate(images["from"]):
242-
image_sn = image["SN"]
257+
# offset the images
258+
for k, image in enumerate(images["from"]):
259+
image_sn = image["SN"]
243260

244-
# starting sample is inclusive, so subtract 1
245-
segmented_net.loc[segmented_net.serialnumber == image_sn, "line"] += starting_lines[k]-1
246-
cnet.to_isis(segmented_net, new_params["ONET"], targetname="moon")
247-
248-
from_originals = [image["Original"] for image in images["from"]]
249-
return {"onet": new_params["ONET"], "original_images": from_originals}
261+
# starting sample is inclusive, so subtract 1
262+
segmented_net.loc[segmented_net.serialnumber == image_sn, "line"] += starting_lines[k]-1
263+
cnet.to_isis(segmented_net, new_params["ONET"], targetname="moon")
264+
265+
from_originals = [image["Original"] for image in images["from"]]
266+
return {"onet": new_params["ONET"], "original_images": from_originals}
267+
return "No Overlap"
250268
else:
251269
return "No Overlap"
252270

@@ -274,7 +292,7 @@ def merge(d1, d2, k):
274292
return v1+v2
275293

276294

277-
def findFeaturesSegment(ui):
295+
def findFeaturesSegment(ui, workdir):
278296
"""
279297
findFeaturesSegment Calls FindFeatures on segmented images
280298
@@ -313,41 +331,69 @@ def findFeaturesSegment(ui):
313331
else:
314332
nthreads = int(multiprocessing.cpu_count())
315333

316-
pool = ThreadPool(ceil(nthreads/len(img_list)))
317-
output = pool.map_async(segment, img_list)
334+
pool = ThreadPool(nthreads)
335+
starmap_args = []
336+
for image in img_list:
337+
starmap_args.append([image, workdir, ui.GetInteger("NL")])
338+
output = pool.starmap_async(segment, starmap_args)
318339
pool.close()
319340
pool.join()
320341
output = output.get()
342+
match_segments = segment(os.path.abspath(ui.GetCubeName("match")), workdir, ui.GetInteger("NL"))
321343

322-
match_segments = segment(ui.GetCubeName("match"), ui.GetInteger("NL"))
323-
from_segments = reduce(lambda d1,d2: {k: merge(d1, d2, k) for k in set(d1)|set(d2)}, output)
344+
if len(img_list) > 1:
345+
from_segments = reduce(lambda d1,d2: {k: merge(d1, d2, k) for k in set(d1)|set(d2)}, output)
346+
else:
347+
# img_list = 1
348+
from_segments = output[0]
349+
for seg, value in from_segments.items():
350+
og_value = value
351+
from_segments[seg] = [og_value]
352+
324353
segment_paths = [s["Path"] for sublist in list(from_segments.values()) for s in sublist]
325354
segment_paths = segment_paths + [s["Path"] for s in list(match_segments.values())]
326355

327356
# re-generate footprints
328-
pool = ThreadPool(ceil(nthreads/len(segment_paths)))
357+
pool = ThreadPool(nthreads)
329358
output = pool.map_async(footprintinit, segment_paths)
330359
pool.close()
331360
pool.join()
332361
output = output.get()
333362
log.debug(f"{output}")
334363

335-
image_sets = list(itertools.product(match_segments.values(), from_segments.values()))
336-
364+
# Remove redundant overlapping pairs
365+
x = match_segments.values()
366+
y = from_segments.values()
367+
# x,y = (x,y) if len(x) > len(y) else (y,x)
368+
image_cartesian = list(itertools.product(x, y))
369+
image_sets = image_cartesian
370+
# for i in image_cartesian:
371+
# if i[0][0]["Segment"] >= i[1]["Segment"]:
372+
# image_sets.append(i)
373+
337374
# restructure things to be easier to manage
338375
job_dicts = []
339376
for im in image_sets:
340377
match = im[0]
341378
from_images = im[1]
379+
if not isinstance(from_images, list):
380+
# from_images must be list type
381+
from_images_list = []
382+
from_images_list.append(from_images)
383+
from_images = from_images_list
342384
job_dicts.append({
343385
"match" : match,
344386
"from" : from_images
345-
})
346-
387+
})
388+
347389
# get params as a dictionary
348390
params = ui.GetParams()
391+
if is_workdir_temp:
392+
params["WORKDIR"] = workdir
349393

350-
pool = ThreadPool(ceil(nthreads/len(job_dicts)))
394+
# findfeatures is already threaded, limit python threads by the maxthreads
395+
# parameter, no maththreads_findfeatures * maxthreads_python <= maxthreads
396+
pool = ThreadPool(int(nthreads/len(job_dicts)))
351397
starmap_args = list(zip([params]*len(job_dicts), job_dicts))
352398
output = pool.starmap_async(generate_cnet, starmap_args)
353399
pool.close()
@@ -369,11 +415,11 @@ def findFeaturesSegment(ui):
369415
tolists = [set(o["original_images"]) for o in output if isinstance(o, dict)]
370416

371417
final_images = set.union(*tolists)
372-
final_images.add(ui.GetCubeName("match"))
418+
final_images.add(os.path.abspath(ui.GetCubeName("match")))
373419

374420
log.debug(f"merged images: {final_images}")
375421
kisis.fromlist.make(final_images, Path(ui.GetFileName("tolist")))
376-
422+
377423
if len(onets) > 1:
378424
try:
379425
kisis.cnetmerge(clist = onet_list, onet=ui.GetFileName("onet"), networkid=ui.GetAsString("networkid"), description=f"{ui.GetString('description')}")
@@ -386,9 +432,23 @@ def findFeaturesSegment(ui):
386432
# Dont merge
387433
shutil.copy(onets[0], ui.GetFileName("onet"))
388434

389-
log.info(f"COMPLETE, wrote { ui.GetFileName("onet")}")
390-
391-
if __name__ == "__main__":
435+
if __name__ == "__main__":
392436
ui = astroset.init_application(sys.argv)
393-
findFeaturesSegment(ui)
394-
437+
is_workdir_temp = not ui.WasEntered("WorkDir")
438+
workdir = Path(tempfile.TemporaryDirectory().name)
439+
if ui.WasEntered("Workdir"):
440+
workdir = Path(ui.GetFileName("Workdir"))
441+
442+
try:
443+
findFeaturesSegment(ui, workdir)
444+
except Exception as e:
445+
if is_workdir_temp:
446+
shutil.rmtree(workdir)
447+
raise e
448+
449+
# log.info(f"COMPLETE, wrote: {ui.GetFileName("onet")}")
450+
if is_workdir_temp:
451+
shutil.rmtree(workdir)
452+
log.info("Complete")
453+
else:
454+
log.info("Intermediate files written to %s", workdir)

isis/python_bindings/apps/findFeaturesSegment/findFeaturesSegment.xml

+14-1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,19 @@
139139
<filter>*.lis</filter>
140140
</parameter>
141141

142+
<parameter name="WORKDIR">
143+
<type>filename</type>
144+
<fileMode>output</fileMode>
145+
<brief>Directory to output intermediate files to</brief>
146+
<description>
147+
This directory is where any intermediate and temp files are saved to.
148+
If this is set to None (default), these files go into the temp directory
149+
which is deleted when the program is terminated. Set this if you want to debug
150+
a network.
151+
</description>
152+
<internalDefault>None</internalDefault>
153+
</parameter>
154+
142155
<parameter name="NL">
143156
<type>integer</type>
144157
<fileMode>output</fileMode>
@@ -1432,4 +1445,4 @@ End
14321445
</example>
14331446
</examples>
14341447

1435-
</application>
1448+
</application>

0 commit comments

Comments
 (0)