def run(self): from xystitch.pto.project import PTOProject '''Take in a list of pto files and merge them into pto''' pto_temp_file = ManagedTempFile.get(None, ".pto") args = ["pto_merge"] args.append("--output=%s" % pto_temp_file) for pto in self.ptos: args.append(pto.get_a_file_name()) print 'MERGING: %s' % (args, ) rc = execute.without_output(args) # go go go if not rc == 0: print print print #print 'Output:' #print output print 'rc: %d' % rc if rc == 35072: # ex: empty projects seem to cause this print 'Out of memory, expect malformed project file' raise Exception('failed pto_merge') if not os.path.exists(str(pto_temp_file)): raise Exception('Output file missing: %s' % (pto_temp_file, )) return PTOProject.from_temp_file(pto_temp_file)
def run_print(pto_fn, stdev=3): print('In: %s' % pto_fn) pto = PTOProject.from_file_name(pto_fn) icm = pto2icm(pto) for ((n_fn, N_fn), (nx, ny), (Nx, Ny)) in gen_cps(pto, icm=icm): print("%s:(%s, %s) %s:(%s, %s)" % (n_fn, nx, ny, N_fn, Nx, Ny)) print(" %s, %s" % (nx - Nx, ny - Ny))
def run(pto_fn, pto_fn_out=None, stdev=3.0): print('In: %s' % pto_fn) bench = Benchmark() # TODO: l/r compensation pto = PTOProject.from_file_name(pto_fn) icm = pto2icm(pto) deltas = [] pairs = [] cpls = [] for cpl, ((n_fn, N_fn), (nx, ny), (Nx, Ny)) in gen_cps(pto, icm=icm): delta = ((nx - Nx)**2 + (ny - Ny)**2)**0.5 deltas.append(delta) pairs.append((n_fn, N_fn)) cpls.append(cpl) if tmpdbg and (n_fn == 'c005_r003.jpg' and N_fn == 'c005_r004.jpg'): print('debug', (n_fn, N_fn), (nx, ny), (Nx, Ny)) print('debug', delta) deltas_u = statistics.mean(deltas) deltas_sd = statistics.stdev(deltas) print("Delta mean: %0.1f" % deltas_u) print("Delta stdev: %0.1f" % deltas_sd) _deltas_min = deltas_u - deltas_sd * stdev deltas_max = deltas_u + deltas_sd * stdev outlier_cps = 0 outlier_pairs = set() pair_cur = None pairi = 0 for cpl, (n_fn, N_fn), delta in zip(cpls, pairs, deltas): if pair_cur != (n_fn, N_fn): pairi = 0 pair_cur = (n_fn, N_fn) else: pairi += 1 # really only care about max if delta > deltas_max: # canonical file names fna, fnb = sorted((n_fn, N_fn)) print("%s %s %u: outlier delta %0.1f" % (fna, fnb, pairi, delta)) outlier_pairs.add((fna, fnb)) outlier_cps += 1 pto.remove_control_point_line(cpl) print("") print("Flagged cps: %u" % outlier_cps) print("Flagged pairs: %u" % len(outlier_pairs)) for fna, fnb in sorted(list(outlier_pairs)): print(" %s %s" % (fna, fnb)) if pto_fn_out: pto.save_as(pto_fn_out) bench.stop() print('Completed in %s' % bench)
def resave_hugin(pto): from xystitch.merger import Merger from xystitch.pto.project import PTOProject # pto_merge -o converted.pto out.pto out.pto blank = PTOProject.from_blank() m = Merger([blank]) m.pto = pto new = m.run(to_pto=True) if new != pto: raise Exception('Expected self merge') dbg('Merge into self')
def pto_unsub(src_prj, sub_image_files, deltas, sub_to_real): ''' Transforms a sub-project back into original control point coordinate space using original file names Returns a new project file src_prj: base project that needs to be transformed sub_image_files: tuple specifying original project 0/1 positions needed to correctly apply deltas deltas: delta to apply to pair_project coordinates to bring back to target (original) project space 0: x 1: y images are relative to each other only has delta within relative image frame, not entire project canvas sub_to_real: map of project file names to target (original) project file names the output project must use these instead of the original names ''' ret = PTOProject.from_simple() same_order = True # Copy/fix images print('Order check') for i, src_il in enumerate(src_prj.get_image_lines()): # copy it dst_il = ImageLine(str(src_il), ret) # fix the name so that it can be merged dst_il.set_name(sub_to_real[src_il.get_name()]) # add it ret.add_image_line(dst_il) same_order = same_order and sub_image_files[ i].file_name == src_il.get_name() print(' %d: %s vs %s' % (i, sub_image_files[i].file_name, src_il.get_name())) # Copy/shift control points # Should have been filtered out earlier if len(src_prj.get_control_point_lines()) == 0: raise Exception('No source control point lines') for src_cpl in src_prj.get_control_point_lines(): # copy it dst_cpl = ControlPointLine(str(src_cpl), ret) # shift to original coordinate space if same_order: # normal adjustment dst_cpl.set_variable('x', src_cpl.get_variable('x') + deltas[0]) dst_cpl.set_variable('y', src_cpl.get_variable('y') + deltas[1]) else: # they got flipped dst_cpl.set_variable('X', src_cpl.get_variable('X') + deltas[0]) dst_cpl.set_variable('Y', src_cpl.get_variable('Y') + deltas[1]) # add it ret.add_control_point_line(dst_cpl) return ret
def generate_core(self, image_file_names): project_file = ManagedTempFile.get(None, ".pto") command = "autopano-sift-c" args = list() # Try to post process them to make them more accurate #args.append("--refine") # Perform RANSAC to try to get bad control points out #args.append("--ransac") #args.append("on") # Unlimited matches args.append("--maxmatches") args.append("0") # ? #args.append("--maxdim") #args.append("10000") # Project file args.append(project_file.file_name) # Images for image_file_name in image_file_names: args.append(image_file_name) # go go go #(rc, output) = Execute.with_output(command, args) (rc, output) = (exc_ret_istr(command, args), '') if not rc == 0: print() print() print() print('output:\n%s' % output) raise Exception('Bad rc: %d' % rc) # We return PTO object, not string return PTOProject.from_temp_file(project_file)
def generate_core(self, image_file_names): command = "autopanoaj" args = list() project_file = ManagedTempFile.get(None, ".pto") # default is .oto args.append("/project:hugin") # Use image args instead of dir args.append("/f") args.append('/path:Z:\\tmp') # Images for image_file_name in image_file_names: args.append(image_file_name.replace("/tmp/", "Z:\\tmp\\")) # go go go #(rc, output) = Execute.with_output(command, args) rc, output = exc_ret_istr(command, args, print_out=True) if not rc == 0: raise Exception('Bad rc: %d' % rc) # We return PTO object, not string # Ditch the gen file because its unreliable shutil.move("/tmp/panorama0.pto", project_file.file_name) f = open(project_file.file_name, 'r') project_text = f.read() # Under WINE, do fixup project_text = project_text.replace('Z:\\tmp\\', '/tmp/') if 0: print() print() print() print(project_text) print() print() print() f.close() f = open(project_file.file_name, 'w') f.write(project_text) return PTOProject.from_temp_file(project_file)
def run(args): if args.threads < 1: raise Exception('Bad threads') print 'Using %d threads' % args.threads log_dir = args.log out_dir = 'out' _dt = logwt(log_dir, 'main.log', shift_d=True) fn = args.pto[0] auto_size = not (args.stp or args.stm or args.stw or args.sth) print 'Assuming input %s is pto project to be stitched' % args.pto project = PTOProject.from_file_name(args.pto) print 'Creating tiler' stp = None if args.stp: stp = mksize(args.stp) elif args.stm: stp = mem2pix(mksize(args.stm)) print 'Memory %s => %s pix' % (args.stm, size2str(stp)) elif auto_size: stm = config.super_tile_memory() if stm: stp = mem2pix(mksize(stm)) # having issues creating very large if stp > 2**32 / 4: # 66 GB max useful as currently written print 'WARNING: reducing to maximum tile size' stp = 2**32 / 4 t = Tiler(project, out_dir, stw=mksize(args.stw), sth=mksize(args.sth), stp=stp, clip_width=args.clip_width, clip_height=args.clip_height, log_dir=log_dir, is_full=args.full) t.threads = args.threads t.verbose = args.verbose t.st_dir = args.st_dir t.out_extension = args.out_ext t.ignore_errors = args.ignore_errors t.ignore_crop = args.ignore_crop t.st_limit = float(args.st_limit) # TODO: make this more proper? if args.nona_args: t.nona_args = args.nona_args.replace('"', '').split(' ') if args.enblend_args: t.enblend_args = args.enblend_args.replace('"', '').split(' ') if args.super_t_xstep: t.super_t_xstep = args.super_t_xstep if args.super_t_ystep: t.super_t_ystep = args.super_t_ystep if args.clip_width: t.clip_width = args.clip_width if args.clip_height: t.clip_height = args.clip_height # if they specified clip but not supertile step recalculate the step so they don't have to do it if args.clip_width or args.clip_height and not (args.super_t_xstep or args.super_t_ystep): t.recalc_step() t.enblend_lock = args.enblend_lock if args.single_dir and not os.path.exists(args.single_dir): os.mkdir(args.single_dir) print('Running tiler') try: t.run() except KeyboardInterrupt: if t.stale_worker: print 'WARNING: forcing exit on stuck worker' time.sleep(0.5) os._exit(1) raise print('Tiler done!') print('Creating single image') single_fn = args.single_fn if single_fn is None: single_fn = 'out.jpg' if args.single_dir: single_fn = os.path.join(args.single_dir, single_fn) # sometimes I restitch with different supertile size # this results in excessive merge, although really I should just delete the old files if 1: print 'Single: using glob strategy on merge' s_fns = glob.glob(os.path.join(args.st_dir, 'st_*x_*y.jpg')) else: print 'Single: using output strategy' s_fns = t.st_fns single_fn_alt = None if args.single_fn is None: single_fn_alt = single_fn.replace('.jpg', '.tif') try: singlify(s_fns, single_fn, single_fn_alt) except HugeImage: print 'WARNING: single: exceeds max image size, skipped'
_outdate = IOTimestamp(sys, 'stdout') _errdate = IOTimestamp(sys, 'stderr') if exist: _outlog.out_fd.write('\n') _outlog.out_fd.write('\n') _outlog.out_fd.write('\n') _outlog.out_fd.write('*' * 80 + '\n') _outlog.out_fd.write('*' * 80 + '\n') _outlog.out_fd.write('*' * 80 + '\n') print 'pr0npto starting' print 'In: %s' % pto_in print 'Out: %s' % pto_out bench = Benchmark() pto = PTOProject.from_file_name(pto_in) # Make sure we don't accidently override the original pto.remove_file_name() config_pto_defaults(pto) if args.center is True: center(pto) if args.anchor: print 'Re-finding anchor' center_anchor(pto) if args.basename: print 'Converting to basename' make_basename(pto)
def main(): parser = argparse.ArgumentParser( description='create tiles from unstitched images') # +/- 2 => 5 x 5 => 25 images # +/- 3 => 7 x 7 => 49 images parser.add_argument('--xydelta', default=3, type=int, help='border size') parser.add_argument('pto', default='out.pto', nargs='?', help='pto project') args = parser.parse_args() pto_orig = PTOProject.from_file_name(args.pto) print("Finding worst image optimization") n, rms = worst_image(pto_orig) print(("got", n, rms)) il = pto_orig.image_lines[n] fn = il.get_name() refrow, refcol = get_row_col(fn) print((fn, refcol, refrow)) img_fns = [] for il in pto_orig.get_image_lines(): img_fns.append(il.get_name()) icm = ImageCoordinateMap.from_tagged_file_names(img_fns) # Delete all ils not in our ROI pto_orig.build_image_fn_map() ils_keep = set() for col, row in iter_center_cr_max(icm, refcol, refrow, args.xydelta): im = icm.get_image(col, row) if im is None: continue ils_keep.add(pto_orig.img_fn2il[im]) ils_del = set(pto_orig.image_lines) - ils_keep print(("%s - %s image lines, keeping %s" % (len(pto_orig.image_lines), len(ils_del), len(ils_keep)))) # Reduced .pto pto_red = pto_orig.copy() print(('Deleting %d / %d images' % (len(ils_del), icm.width() * icm.height()))) pto_red.del_images(ils_del) print((len(pto_orig.image_lines), len(pto_red.image_lines))) print("Centering...") center(pto_red) print("Set XY var optimization mode...") optimize_xy_only(pto_red) # p w25989 h25989 f0 v165 n"TIFF_m c:LZW" E0.0 R0 S"514,25955,8128,32815" pto_red.get_panorama_line().uncrop() print("Saving preliminary project...") pto_red_fn = pto_orig.file_name.replace('.pto', '_sm.pto') pto_red.save_as(pto_red_fn, is_new_filename=True) print("Fitting FOV") subprocess.check_call("pano_modify --fov=AUTO --canvas=AUTO -o %s %s" % (pto_red_fn, pto_red_fn), shell=True) print(('Opening temp file %s' % pto_red.file_name)) subp = subprocess.Popen(['hugin', pto_red.file_name], shell=False) subp.communicate() print(('Hugin exited with code %d' % subp.returncode)) red_orig_ncpls = len(pto_red.control_point_lines) pto_red.reopen() red_new_ncpls = len(pto_red.control_point_lines) # Filter control point lines # Delete all control points associated with subproject # Then import all new control points print("Deleting stale control points") # Convert image lines to indices iln_keep = set() orig_ncpls = len(pto_orig.control_point_lines) for iln, il in enumerate(pto_orig.image_lines): if il in ils_keep: iln_keep.add(iln) # Filter out any control point lines that were within our ROI cpls_new = [] for cpl in pto_orig.control_point_lines: n = cpl.get_variable("n") N = cpl.get_variable("N") if not (n in iln_keep and N in iln_keep): cpls_new.append(cpl) # Shift into main object, discarding munged cpls print(("cpl filtering %u => %u" % (len(pto_orig.control_point_lines), len(cpls_new)))) pto_orig.control_point_lines = cpls_new red_fn2il = pto_red.build_image_fn_map() red_il2fn = dict([(v, k) for k, v in list(red_fn2il.items())]) red_il2i = pto_red.build_il2i() red_i2il = dict([(v, k) for k, v in list(red_il2i.items())]) full_fn2il = pto_orig.build_image_fn_map() full_il2i = pto_orig.build_il2i() def iln_red2orig(nred): fn = red_il2fn[red_i2il[nred]] return full_il2i[full_fn2il[fn]] # Now add new cpls in # Be very careful translating image indices print("Adding new control points") for cpl in pto_red.control_point_lines: n = cpl.get_variable("n") n2 = iln_red2orig(n) cpl.set_variable('n', n2) N = cpl.get_variable("N") N2 = iln_red2orig(N) cpl.set_variable('N', N2) # print("cpl n=%u N=%u => n=%u N=%u" % (n, N, n2, N2)) cpl.project = pto_orig pto_orig.control_point_lines.append(cpl) print(("image lines", len(pto_orig.image_lines), len(pto_red.image_lines))) print('Saving final project') # small backup in case something went wrong shutil.copy(pto_orig.file_name, 'cphugin_old.pto') pto_orig.save_as(pto_orig.file_name) new_ncpls = len(pto_orig.control_point_lines) print(("roi %u => %u cpls" % (red_orig_ncpls, red_new_ncpls))) print(("pto %u => %u cpls" % (orig_ncpls, new_ncpls))) print('Done!')
from xystitch.image_coordinate_map import ImageCoordinateMap import subprocess import shutil if __name__ == "__main__": parser = argparse.ArgumentParser( description='create tiles from unstitched images') parser.add_argument('--border', default='1', help='border size') parser.add_argument('pto', default='out.pto', nargs='?', help='pto project') args = parser.parse_args() args.border = int(args.border, 0) pto_orig = PTOProject.from_file_name(args.pto) img_fns = [] for il in pto_orig.get_image_lines(): img_fns.append(il.get_name()) icm = ImageCoordinateMap.from_tagged_file_names(img_fns) # Reduced .pto pto_red = pto_orig.copy() # Delete all lines not in the peripheral pto_orig.build_image_fn_map() ils_del = [] for y in xrange(args.border, icm.height() - args.border): for x in xrange(args.border, icm.width() - args.border): im = icm.get_image(x, y) if im is None: continue
default=True, help='Allow missing images') parser.add_argument('--border', action="store_true", dest="border", default=False, help='Manually optimize border') args = parser.parse_args() pto_ref_fn = args.pto_ref[0] pto_out_fn = args.pto_out print('Reference in: %s' % pto_ref_fn) print('Out: %s' % pto_out_fn) # Have to start somewhere... pto_out = PTOProject.from_default() # Add the images in for image in args.images: pto_out.add_image(image) pto_ref = PTOProject.from_file_name(pto_ref_fn) pto_ref.remove_file_name() linear_reoptimize(pto_out, pto_ref, allow_missing=args.allow_missing, order=2, border=args.border) print('Centering...') center(pto_out)
def generate_core(self, img_fns): # cpfind (and likely cpclean) trashes absolute file names # we need to restore them so that tools recognize the file names real_fn_base2full = {} args = list() project = PTOProject.from_default2() fn_obj = ManagedTempFile.get(None, ".pto") project.set_file_name(fn_obj.file_name) # Start with cpfind args.append("--multirow") args.append("--fullscale") # output file args.append("-o") args.append(project.file_name) # input file args.append(project.file_name) # Images for img_fn in img_fns: # xxx: why do we take the realpath? real_fn = os.path.realpath(img_fn) real_fn_base2full[os.path.basename(real_fn)] = img_fn project.add_image(real_fn, def_opt=True) project.save() print() print() print() print(project.get_text()) print() print() print() #(rc, output) = Execute.with_output('cpfind', args, print_output=self.print_output) print('cpfind' + ' '.join(args)) (rc, output) = exc_ret_istr('cpfind', args, print_out=self.print_output) print('PanoCP: cpfind done') if not rc == 0: print() print() print() print('output:') print(output) print() # Happens very rarely # 2018-01-24T04:00:18.720954: Exception: Bad rc: -11 # Log it but consider it a known failure if rc == -11: return None raise Exception('Bad rc: %d' % rc) # Now run cpclean args = list() # output file args.append("-o") args.append(project.file_name) # input file args.append(project.file_name) (rc, output) = exc_ret_istr('cpclean', args, print_out=self.print_output) print('PanoCP: cpclean done') if not rc == 0: print() print() print() print('output:') print(output) print() raise Exception('Bad rc: %d' % rc) project.reopen() print('Fixing image lines...') for il in project.image_lines: src = il.get_name() dst = real_fn_base2full[src] print(' %s => %s' % (src, dst)) il.set_name(dst) project.set_file_name(None) fn_obj = None # Will happen if failed to match # be optimistic: cpclean work will be wasted but avoids parsing project twice if len(project.get_control_point_lines()) == 0: print('WARNING: failed') return None return project
def run(args): log_dir = args.log out_dir = 'out' _dt = logwt(log_dir, 'main.log', shift_d=True) fn = args.pto[0] auto_size = not (args.stp or args.stm or args.stw or args.sth) if args.threads < 1: raise Exception('Bad threads') print(('Using %d threads' % args.threads)) print(('Loading %s' % args.pto)) project = PTOProject.from_file_name(args.pto) print('Creating tiler') t = Tiler(project, out_dir, stw=mksize(args.stw), sth=mksize(args.sth), stp=None, clip_width=args.clip_width, clip_height=args.clip_height, log_dir=log_dir, is_full=args.full) t.threads = args.threads t.verbose = args.verbose t.st_dir = args.st_dir t.force = args.force t.merge = args.merge t.out_extension = args.out_ext t.ignore_errors = args.ignore_errors t.ignore_crop = args.ignore_crop t.st_limit = float(args.st_limit) # TODO: make this more proper? if args.nona_args: t.nona_args = args.nona_args.replace('"', '').split(' ') if args.enblend_args: t.enblend_args = args.enblend_args.replace('"', '').split(' ') if args.super_t_xstep: t.super_t_xstep = args.super_t_xstep if args.super_t_ystep: t.super_t_ystep = args.super_t_ystep t.enblend_lock = args.enblend_lock if args.single_dir and not os.path.exists(args.single_dir): os.mkdir(args.single_dir) t.calc_expected_tiles() t.calc_vars() print('Forcing tiler on all images') for fn in glob.glob(args.st_dir + "/*.jpg"): print("") print(("%s" % fn)) im = Image.open(fn) width, height = im.size x0, y0 = coord(fn) #t.make_tile(im, x, y, row, col) st_bounds = [x0, x0 + width, y0, y0 + height] t.process_image(fn, im, st_bounds)
def run(args): log_dir = args.log out_dir = 'out' _outlog, _errlog, outdate, _errdate = logwt(log_dir, 'main.log', shift_d=True) worker_stdout = outdate.fd bench = Benchmark() try: print('Assuming input %s is pto project to be stitched' % args.pto) project = PTOProject.from_file_name(args.pto) print('Creating tiler') threads, stp = make_threads_stp(args) t = Tiler(pto=project, out_dir=out_dir, stw=mksize(args.stw), sth=mksize(args.sth), stp=stp, clip_width=args.clip_width, clip_height=args.clip_height, log_dir=log_dir, is_full=args.full, dry=args.dry, worker_stdout=worker_stdout) t.set_threads(threads) t.set_verbose(args.verbose) t.set_st_dir(args.st_dir) t.set_out_extension(args.out_ext) t.set_ignore_errors(args.ignore_errors) t.set_ignore_crop(args.ignore_crop) t.set_st_limit(float(args.st_limit)) # TODO: make this more proper? if args.nona_args: t.nona_args = args.nona_args.replace('"', '').split(' ') if args.enblend_args: t.enblend_args = args.enblend_args.replace('"', '').split(' ') if args.super_t_xstep: t.set_super_t_xstep(args.super_t_xstep) if args.super_t_ystep: t.set_super_t_ystep(args.super_t_ystep) if args.clip_width: t.set_clip_width(args.clip_width) if args.clip_height: t.set_clip_height(args.clip_height) # if they specified clip but not supertile step recalculate the step so they don't have to do it if args.clip_width or args.clip_height and not (args.super_t_xstep or args.super_t_ystep): t.recalc_step() t.set_enblend_lock(args.enblend_lock) if args.single_dir and not os.path.exists(args.single_dir): os.mkdir(args.single_dir) config.set_enblend_safer_mode(args.safer_mode) config.set_enblend_safest_mode(args.safest_mode) print('Running tiler') try: t.run() except KeyboardInterrupt: if t.stale_worker: print('WARNING: forcing exit on stuck worker') time.sleep(0.5) os._exit(1) raise print('Tiler done!') print('Creating single image') single_fn = args.single_fn if single_fn is None: single_fn = 'out.jpg' if args.single_dir: single_fn = os.path.join(args.single_dir, single_fn) # sometimes I restitch with different supertile size # this results in excessive merge, although really I should just delete the old files if 1: print('Single: using glob strategy on merge') s_fns = glob.glob(os.path.join(args.st_dir, 'st_*x_*y.jpg')) else: print('Single: using output strategy') s_fns = t.st_fns single_fn_alt = None if args.single_fn is None: single_fn_alt = single_fn.replace('.jpg', '.tif') try: singlify(s_fns, single_fn, single_fn_alt) except HugeImage: print('WARNING: single: exceeds max image size, skipped') finally: bench.stop() print('Completed in %s' % bench)
def run(self): if self.dry: print 'Dry run abort' return bench = Benchmark() if not self.output_project_file_name: raise Exception("need project file") #if not self.output_project_file_name: #self.project_temp_file = ManagedTempFile.get() #self.output_project_file_name = self.project_temp_file.file_name print 'Beginning stitch' print 'output project file name: %s' % self.output_project_file_name #sys.exit(1) self.init_failures() # Generate control points and merge them into a master project self.control_point_gen = get_cp_engine() # How many rows and cols to go to each side # If you hand took the pictures, this might suit you self.project = PTOProject.from_blank() if self.output_project_file_name: self.project.set_file_name(self.output_project_file_name) if os.path.exists(self.output_project_file_name): # Otherwise, we merge into it print 'WARNING: removing old project file: %s' % self.output_project_file_name os.remove(self.output_project_file_name) else: self.project.get_a_file_name(None, "_master.pto") self.project.image_file_names = self.image_file_names try: ''' Generate control points ''' self.generate_control_points() print 'Soften try: %s' % (self.soften_try, ) print 'Soften ok: %s' % (self.soften_ok, ) print 'Post stitch fixup...' optimize_xy_only(self.project) fixup_i_lines(self.project) fixup_p_lines(self.project) print print '***PTO project baseline final (%s / %s) data length %d***' % ( self.project.file_name, self.output_project_file_name, len(self.project.get_text())) print self.failure_json_w() print # Make dead sure its saved up to date self.project.save() # having issues with this.. if self.output_project_file_name and not self.project.file_name == self.output_project_file_name: raise Exception('project file name changed %s %s', self.project.file_name, self.output_project_file_name) # TODO: missing calc opt size/width/height/fov and crop except Exception as e: sys.stdout.flush() sys.stderr.flush() print print 'WARNING: stitch FAILED' traceback.print_exc() try: fn = self.project.file_name + ".failed" print 'Attempting to save intermediate result to %s' % fn self.project.save_as(fn) except: print 'WARNING: failed intermediate save' raise e finally: bench.stop() print 'Stitch done in %s' % bench
def generate_control_points(self): ''' Generate control points Generate to all neighbors to start with ''' print 'PHASE 1: adjacent images' cur_x = 0.0 cur_y = 0.0 x_delta = None y_delta = None # Eliminate special case from main loop for pair in self.linear_pairs_gen(): self.spatial_map.add_point(cur_y, cur_x, pair[0]) break n_pairs = len(set(self.linear_pairs_gen())) cur_pair_index = 0 for pair in self.linear_pairs_gen(): cur_pair_index += 1 print 'Working on %s (%d / %d)' % (repr(pair), cur_pair_index, n_pairs) result = self.analyze_image_pair(pair) if result is None: ''' Two situations: Early on: best guess is to go direction of last Also simple to implement, try always for now If we are at an edge (turn point) we are in trouble For large images this should be a minority if all images are equally likely to have issues Edges might have easier feature detection? Or worse since void Better: calculate average length and see how far we are along in the row Make a guess as to whether we should turn or not ''' print 'Attempting error recovery' # Leave values untouched to save from last loop value # If we failed on the first pass give up if x_delta is None or y_delta is None: raise Exception('Die') print 'Using last delta values: y=%f, x=%f' % (y_delta, x_delta) else: # Common / expected case (x_delta, y_delta) = result cur_x += x_delta cur_y += y_delta # Note we must add the estimate even if its not known self.spatial_map.add_point(cur_y, cur_x, pair[1]) print 'Created %d sub projects' % len(self.sub_projects) phase_1_project = PTOProject.from_blank() print 'Sub projects (full image):' for project in self.sub_projects: # prefix so I can grep it for debugging print '\tSUB: ' + project.file_name phase_1_project.merge_into(self.sub_projects) # Save is more of debug thing now...helps analyze crashes phase_1_project.get_a_file_name() phase_1_project.save() print print print print phase_1_project.text print print print print 'Master project file: %s' % phase_1_project.file_name print 'PHASE 1: done' print 'PHASE 2: fortify' fortify_stitch = FortifyStitch.from_wander(phase_1_project, self.image_file_names, self.tried_pairs, self.spatial_map) fortify_stitch.set_output_project_file_name(self.project.file_name) fortify_stitch.run() self.project = fortify_stitch.project print 'PHASE 2: done'