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 get_a_file_name(self, prefix=None, postfix=None): '''Return a file name that has current .pto contents''' '''If doesn't have a real file, create a temp file''' if self.file_name: return self.file_name if postfix is None: postfix = ".pto" self.temp_file = ManagedTempFile.get(prefix, postfix) self.file_name = self.temp_file.file_name # hmmm... self.save() return self.file_name
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 hugin_form(self): ''' This is used when merging through fortify stitch Something is causing pto_merge to hang, but NOT ptomerge Only occurs if I wrap my commands in a script... The script doesn't do any fancy I/O redirection clear rm -rf /tmp/xystitch_* pr0nstitch *.jpg out.pto pto_merge produces nicer output than ptomerge While ptomerge produces the fields I need, it leaves some other junk I think pto_merge also calculates width/heigh attributes part of Hugin [mcmaster@gespenst first]$ pto_merge Warning: pto_merge requires at least 2 project files pto_merge: merges several project files pto_merge version 2010.4.0.854952d82c8f part of perl-Panotools-Script [mcmaster@gespenst first]$ ptomerge --help cannot read-open --help at /usr/share/perl5/Panotools/Script.pm line 91. man ptomerge ... ptomerge infile1.pto infile2.pto infile3.pto [...] outfile.pto ... ''' # However, this tool also generates an archaic .pto format that pto can parse, but I don't want to # pretend to merge into an empty project to force Hugin to clean it up # pto_merge --output=temp.pto /dev/null temp.pto if False: args = list() args.append("%s" % self.get_a_file_name()) args.append("%s" % self.get_a_file_name()) args.append("%s" % self.get_a_file_name()) (rc, output) = Execute.with_output("ptomerge", args) else: args = list() args.append("--output=%s" % self.get_a_file_name()) args.append("%s" % self.get_a_file_name()) if False: args.append("/dev/null") else: empty_file = ManagedTempFile.get(None, ".pto") open(empty_file.file_name, 'w').write('') args.append(empty_file.file_name) (rc, output) = Execute.with_output("pto_merge", args) if not rc == 0: print print print if rc == 35072: # ex: empty projects seem to cause this print 'Out of memory, expect malformed project file' print 'output:%s' % output raise Exception('Bad rc: %d' % rc) self.reopen()
def control_points_by_subimage(self, pair, image_fn_pair): '''Stitch two images together by cropping to restrict overlap''' # subimage_factor: (y, x) overlap percent tuple or none for default # pair: pair of row/col or coordinate positions (used to determine relative positions) # (0, 0) at upper left # image_fn_pair: pair of image file names print 'Preparing subimage stitch on %s:%s' % (image_fn_pair[0], image_fn_pair[1]) ''' Just work on the overlap section, maybe even less ''' images = [ PImage.from_file(image_file_name) for image_file_name in image_fn_pair ] ''' image_0 used as reference 4 basic situations: left, right, up right 8 extended: 4 basic + corners Pairs should be sorted, which simplifies the logic ''' sub_image_0_x_delta = 0 sub_image_0_y_delta = 0 sub_image_1_x_end = images[1].width() sub_image_1_y_end = images[1].height() # Add some backlash margin # "more overlap" means will try a slightly larger area #margin = 0.05 x_overlap = self.x_overlap y_overlap = self.y_overlap # image 0 left of image 1? if pair.first.col < pair.second.col: # Keep image 0 right, image 1 left sub_image_0_x_delta = int(images[0].width() * x_overlap) sub_image_1_x_end = int( round(images[1].width() * (1.0 - x_overlap))) # image 0 above image 1? if pair.first.row < pair.second.row: # Keep image 0 top, image 1 bottom sub_image_0_y_delta = int(images[0].height() * y_overlap) sub_image_1_y_end = int( round(images[1].height() * (1.0 - y_overlap))) ''' print 'image 0 x delta: %d, y delta: %d' % (sub_image_0_x_delta, sub_image_0_y_delta) Note y starts at top in PIL ''' sub_image_0 = images[0].subimage(sub_image_0_x_delta, None, sub_image_0_y_delta, None) sub_image_1 = images[1].subimage(None, sub_image_1_x_end, None, sub_image_1_y_end) sub_image_0_file = ManagedTempFile.get(None, '.jpg') sub_image_1_file = ManagedTempFile.get(None, '.jpg') print 'sub image 0: width=%d, height=%d, name=%s' % (sub_image_0.width( ), sub_image_0.height(), sub_image_0_file.file_name) print 'sub image 1: width=%d, height=%d, name=%s' % (sub_image_1.width( ), sub_image_1.height(), sub_image_1_file.file_name) #sys.exit(1) sub_image_0.image.save(sub_image_0_file.file_name) sub_image_1.image.save(sub_image_1_file.file_name) sub_image_fn_pair = (sub_image_0_file.file_name, sub_image_1_file.file_name) # subimage file name symbolic link to subimage file name # this should be taken care of inside of control point actually #sub_link_to_sub = dict() # subimage to the image it came from sub_to_real = dict() sub_to_real[sub_image_0_file.file_name] = image_fn_pair[0] sub_to_real[sub_image_1_file.file_name] = image_fn_pair[1] # Returns a pto project object pair_project = self.control_point_gen.generate_core(sub_image_fn_pair) if pair_project is None: print 'WARNING: failed to gen control points @ %s' % repr(pair) return None # all we need to do is adjust xy positions # afaik above is way overcomplicated final_pair_project = pto_unsub( pair_project, (sub_image_0_file, sub_image_1_file), (sub_image_0_x_delta, sub_image_0_y_delta), sub_to_real) # Filenames become absolute #sys.exit(1) return final_pair_project
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