def resave_hugin(pto): from pr0ntools.stitch.merger import Merger from pr0ntools.stitch.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') print 'Merge into self'
def resave_hugin(pto): from pr0ntools.stitch.merger import Merger from pr0ntools.stitch.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 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'
def run(self): if self.dry: print 'Dry run abort' return if not self.output_project_file_name and not self.output_image_file_name: raise Exception("need either project or image 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 print 'output image file name: %s' % self.output_image_file_name #sys.exit(1) self.init_failures() # Generate control points and merge them into a master project self.control_point_gen = ControlPointGenerator() # 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() if False: self.photometric_optimizer = PhotometricOptimizer(self.project) self.photometric_optimizer.run() # Remove statistically unpleasant points if False: self.cleaner = PTOClean(self.project) self.cleaner.run() print 'Post stitch fixup...' optimize_xy_only(self.project) fixup_i_lines(self.project) fixup_p_lines(self.project) if 0: center_anchor(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 if self.failures: print 'Writing failure JSON' cc = self.failures.critical_count() print '%d pairs failed to make %d images critical' % (self.failures.pair_count(), cc) if cc: print '******WARNING WARNING WARING******' print '%d images are not connected' % cc print '******WARNING WARNING WARING******' open('stitch_failures.json', 'w').write(str(self.failures)) 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) self.optimize = False if self.optimize: self.optimizer = optimizer.PTOptimizer(self.project) self.optimizer.run() center(self.project) # TODO: missing calc opt size/width/height/fov and crop # Did we request an actual stitch? if self.output_image_file_name: print 'Stitching...' self.remapper = Remapper(self.project) self.remapper.remap(self.output_image_file_name) else: print 'NOT stitching (common stitch)' except Exception as e: print print 'WARNING: stitch FAILED' 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
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 partial_optimize(self, xo0, xo1, yo0, yo1, xf0=0, xf1=0, yf0=0, yf1=0): ''' Return a PTOptimizer optimized sub-self.opt_project o: optimized row/col f: fixed row/col relative to counterpart allows to join in with pre-optimized rows/cols ''' print 'Optimizing base region' print 'Selected base region x(%d:%d), y(%d:%d)' % (xo0, xo1, yo0, yo1) if xf0 > 0: raise Exception('') if xf1 < 0: raise Exception('') if yf0 > 0: raise Exception('') if yf1 < 0: raise Exception('') xf0 += xo0 xf1 += xo1 yf0 += yo0 yf1 += yo1 ''' # Remove all previously selected optimizations project.variable_lines = [] # Mark the selected images for optimization for col in xrange(xo0, xo1 + 1, 1): for row in xrange(yo0, yo1 + 1, 1): fn = self.icm.get_image(col, row) img_i = self.project.img_fn2i(fn) vl = VariableLine('v d%d e%d' % (img_i, img_i), project) project.variable_lines.append(vl) ''' project = PTOProject.from_blank() # Copy special lines # in particular need to keep canvas scale project.set_pano_line_by_text(str(self.opt_project.panorama_line)) project.set_mode_line_by_text(str(self.opt_project.mode_line)) # Copy in image lines # Create a set of all images of interest to make relevant lines easy to find rel_i = set() for col in xrange(xf0, xf1 + 1, 1): for row in xrange(yf0, yf1 + 1, 1): fn = self.icm.get_image(col, row) il = self.project.img_fn2l(fn) rel_i.add(il.get_index()) # Image itself project.image_lines.append(ImageLine(str(il), project)) # save indices to quickly eliminate/replace them cpl_is = [] # Now that all images are added we can add features between them for cpli, cpl in enumerate(self.opt_project.get_control_point_lines()): # c n1 N0 x121.0 y258.0 X133.0 Y1056.0 t0 n = cpl.get_variable('n') N = cpl.get_variable('N') if n in rel_i and N in rel_i: cpl2 = ControlPointLine(str(cpl), project) # Indexes will be different, adjust accordingly cpl2.set_variable('n', project.i2i(self.opt_project, n)) cpl2.set_variable('N', project.i2i(self.opt_project, N)) project.control_point_lines.append(cpl2) cpl_is.append(cpli) anchor = None # All variable? if xo0 == xf0 and xo1 == xf1 and yo0 == yf0 and yo1 == yf1: # Then must anchor solution to a fixed tile anchor = ((xo0 + xo1) / 2, (xf0 + xf1) / 2) # Finally, set images to optimize (XY only) for col in xrange(xo0, xo1 + 1, 1): for row in xrange(yo0, yo1 + 1, 1): # Don't optimize if its the fixed image if (col, row) == anchor: continue fn = self.icm.get_image(col, row) img_i = project.img_fn2i(fn) vl = VariableLine('v d%d e%d' % (img_i, img_i), project) project.variable_lines.append(vl) # In case it crashes do a debug dump pre_run_text = project.get_text() if 0: print project.variable_lines print print print 'PT optimizer project:' print pre_run_text print print raise Exception('Debug break') # "PToptimizer out.pto" args = ["PToptimizer"] args.append(project.get_a_file_name()) #project.save() rc = execute.without_output(args) if rc != 0: fn = '/tmp/pr0nstitch.optimizer_failed.pto' print print print 'Failed rc: %d' % rc print 'Failed project save to %s' % (fn, ) try: open(fn, 'w').write(pre_run_text) except: print 'WARNING: failed to write failure' print print raise Exception('failed position optimization') # API assumes that projects don't change under us project.reopen() ''' Line looks like this # final rms error 24.0394 units ''' rms_error = None for l in project.get_comment_lines(): if l.find('final rms error') >= 00: rms_error = float(l.split()[4]) break print 'Optimize: RMS error of %f' % rms_error # Filter out gross optimization problems if self.rms_error_threshold and rms_error > self.rms_error_threshold: raise Exception("Max RMS error threshold %f but got %f" % (self.rms_error_threshold, rms_error)) if self.debug: print 'Parsed: %s' % str(project.parsed) if self.debug: print print print print 'Optimized project:' print project #sys.exit(1) ret = self.opt_project.copy() print 'Optimized project parsed: %d' % self.opt_project.parsed print 'Merging project...' merge_opt_pto(project, ret) ret.save_as('fixup.pto') return (ret, cpl_is)
def partial_optimize(self, xo0, xo1, yo0, yo1, xf0=0, xf1=0, yf0=0, yf1=0): ''' Return a PTOptimizer optimized sub-self.opt_project o: optimized row/col f: fixed row/col relative to counterpart allows to join in with pre-optimized rows/cols ''' print 'Optimizing base region' print 'Selected base region x(%d:%d), y(%d:%d)' % (xo0, xo1, yo0, yo1) if xf0 > 0: raise Exception('') if xf1 < 0: raise Exception('') if yf0 > 0: raise Exception('') if yf1 < 0: raise Exception('') xf0 += xo0 xf1 += xo1 yf0 += yo0 yf1 += yo1 ''' # Remove all previously selected optimizations project.variable_lines = [] # Mark the selected images for optimization for col in xrange(xo0, xo1 + 1, 1): for row in xrange(yo0, yo1 + 1, 1): fn = self.icm.get_image(col, row) img_i = self.project.img_fn2i(fn) vl = VariableLine('v d%d e%d' % (img_i, img_i), project) project.variable_lines.append(vl) ''' project = PTOProject.from_blank() # Copy special lines # in particular need to keep canvas scale project.set_pano_line_by_text(str(self.opt_project.panorama_line)) project.set_mode_line_by_text(str(self.opt_project.mode_line)) # Copy in image lines # Create a set of all images of interest to make relevant lines easy to find rel_i = set() for col in xrange(xf0, xf1 + 1, 1): for row in xrange(yf0, yf1 + 1, 1): fn = self.icm.get_image(col, row) il = self.project.img_fn2l(fn) rel_i.add(il.get_index()) # Image itself project.image_lines.append(ImageLine(str(il), project)) # save indices to quickly eliminate/replace them cpl_is = [] # Now that all images are added we can add features between them for cpli, cpl in enumerate(self.opt_project.get_control_point_lines()): # c n1 N0 x121.0 y258.0 X133.0 Y1056.0 t0 n = cpl.get_variable('n') N = cpl.get_variable('N') if n in rel_i and N in rel_i: cpl2 = ControlPointLine(str(cpl), project) # Indexes will be different, adjust accordingly cpl2.set_variable('n', project.i2i(self.opt_project, n)) cpl2.set_variable('N', project.i2i(self.opt_project, N)) project.control_point_lines.append(cpl2) cpl_is.append(cpli) anchor = None # All variable? if xo0 == xf0 and xo1 == xf1 and yo0 == yf0 and yo1 == yf1: # Then must anchor solution to a fixed tile anchor = ((xo0 + xo1) / 2, (xf0 + xf1) / 2) # Finally, set images to optimize (XY only) for col in xrange(xo0, xo1 + 1, 1): for row in xrange(yo0, yo1 + 1, 1): # Don't optimize if its the fixed image if (col, row) == anchor: continue fn = self.icm.get_image(col, row) img_i = project.img_fn2i(fn) vl = VariableLine('v d%d e%d' % (img_i, img_i), project) project.variable_lines.append(vl) # In case it crashes do a debug dump pre_run_text = project.get_text() if 0: print project.variable_lines print print print 'PT optimizer project:' print pre_run_text print print raise Exception('Debug break') # "PToptimizer out.pto" args = ["PToptimizer"] args.append(project.get_a_file_name()) #project.save() rc = execute.without_output(args) if rc != 0: fn = '/tmp/pr0nstitch.optimizer_failed.pto' print print print 'Failed rc: %d' % rc print 'Failed project save to %s' % (fn,) try: open(fn, 'w').write(pre_run_text) except: print 'WARNING: failed to write failure' print print raise Exception('failed position optimization') # API assumes that projects don't change under us project.reopen() ''' Line looks like this # final rms error 24.0394 units ''' rms_error = None for l in project.get_comment_lines(): if l.find('final rms error') >= 00: rms_error = float(l.split()[4]) break print 'Optimize: RMS error of %f' % rms_error # Filter out gross optimization problems if self.rms_error_threshold and rms_error > self.rms_error_threshold: raise Exception("Max RMS error threshold %f but got %f" % (self.rms_error_threshold, rms_error)) if self.debug: print 'Parsed: %s' % str(project.parsed) if self.debug: print print print print 'Optimized project:' print project #sys.exit(1) ret = self.opt_project.copy() print 'Optimized project parsed: %d' % self.opt_project.parsed print 'Merging project...' merge_opt_pto(project, ret) ret.save_as('fixup.pto') return (ret, cpl_is)
def run(self): if not self.output_project_file_name and not self.output_image_file_name: raise Exception("need either project or image 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 'output project file name: %s' % self.output_project_file_name print 'output image file name: %s' % self.output_image_file_name #sys.exit(1) # Generate control points and merge them into a master project self.control_point_gen = ControlPointGenerator() # 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 ''' Generate control points ''' self.generate_control_points() if False: self.photometric_optimizer = PhotometricOptimizer(self.project) self.photometric_optimizer.run() # Remove statistically unpleasant points if False: self.cleaner = PTOClean(self.project) self.cleaner.run() self.project.optimize_xy_only() print 'Fixing up i (image attributes) lines...' new_project_text = '' new_lines = '' for line in self.project.text.split('\n'): if line == '': new_project_text += '\n' elif line[0] == 'i': # before replace # i Eb1 Eev0 Er1 Ra0.0111006880179048 Rb-0.00838561356067657 Rc0.0198899246752262 Rd0.0135543448850513 Re-0.0435801632702351 Va1 Vb0.366722181378024 Vc-1.14825880321425 Vd0.904996105280657 Vm5 Vx0 Vy0 a0 b0 c0 d0 e0 f0 g0 h2112 n"x00000_y00033.jpg" p0 r0 t0 v70 w2816 y0 new_line = '' for part in line.split(): if part[0] == 'i': new_line += part # Force lense type 0 (rectilinear) # Otherwise, it gets added as -2 if we are unlucky ("Error on line 6") # or 2 (fisheye) if we are lucky (screwed up image) new_line += ' f0' # Keep image file name elif part[0] == 'n': new_line += ' ' + part # Script is getting angry, try to slim it up else: print 'Skipping unknown garbage: %s' % part new_project_text += new_line + '\n' else: new_project_text += line + '\n' self.project.text = new_project_text print print print self.project.text print print ''' f0: rectilinear f2: equirectangular # p f2 w8000 h24 v179 E0 R0 n"TIFF_m c:NONE" # p f0 w8000 h24 v179 E0 R0 n"TIFF_m c:NONE" ''' print 'Fixing up single lines' new_project_text = '' for line in self.project.text.split('\n'): if line == '': new_project_text += '\n' elif line[0] == 'p': new_line = '' for part in line.split(): if part[0] == 'p': new_line += 'p' elif part[0] == 'f': new_line += ' f0' else: new_line += ' ' + part new_project_text += new_line + '\n' else: new_project_text += line + '\n' self.project.text = new_project_text print print print self.project.text print print print print '***PTO project final (%s / %s) data length %d***' % (self.project.file_name, self.output_project_file_name, len(self.project.get_text())) 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) # Did we request an actual stitch? if self.output_image_file_name: print 'Stitching...' self.remapper = Remapper(self.project, self.output_image_file_name) self.remapper.run() else: print 'NOT stitching'