def run(self): from pr0ntools.stitch.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 do_merge(self, others): pto_temp_file = ManagedTempFile.get(None, ".pto") command = "pto_merge" args = list() args.append("--output=%s" % pto_temp_file) # Possible this is still empty if self.file_name and os.path.exists(self.file_name): args.append(self.file_name) for other in others: args.append(other.get_a_file_name()) print_debug(args) (rc, output) = Execute.with_output(command, args) # go go go if not rc == 0: print print print print 'Output:' print output print 'rc: %d' % rc raise Exception('failed pto_merge') return PTOProject.from_temp_file(pto_temp_file)
def run(self): from pr0ntools.stitch.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 do_get_a_file_name(self, prefix = None, postfix = None): '''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 return self.file_name
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 run(self, to_pto = False): from pr0ntools.stitch.pto.project import PTOProject others = self.files pto = self.pto '''Take in a list of pto files and merge them into pto''' if to_pto: pto_temp_file = self.pto.get_a_file_name() else: pto_temp_file = ManagedTempFile.get(None, ".pto") command = "pto_merge" args = list() args.append("--output=%s" % pto_temp_file) # Possible this is still empty if pto.file_name and os.path.exists(pto.file_name): args.append(pto.file_name) for other in others: args.append(other.get_a_file_name()) print_debug(args) (rc, output) = Execute.with_output(command, 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 to_pto: self.pto.reopen() return self.pto else: return PTOProject.from_temp_file(pto_temp_file)
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 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 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 hugin_form(self): ''' 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/pr0ntools_* 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 print 'output:%s' % output raise Exception('Bad rc: %d' % rc) self.reopen()
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) (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 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 try_supertile(self, st_bounds): '''x0/1 and y0/1 are global absolute coordinates''' # First generate all of the valid tiles across this area to see if we can get any useful work done? # every supertile should have at least one solution or the bounds aren't good x0, x1, y0, y1 = st_bounds bench = Benchmark() try: if self.st_dir: # nah...tiff takes up too much space dst = os.path.join(self.st_dir, 'st_%06dx_%06dy.jpg' % (x0, y0)) if os.path.exists(dst): # normally this is a .tif so slight loss in quality img = PImage.from_file(dst) print 'supertile short circuit on already existing: %s' % ( dst, ) return img # st_081357x_000587y.jpg temp_file = ManagedTempFile.get(None, '.tif', prefix_mangle='st_%06dx_%06dy_' % (x0, y0)) stitcher = PartialStitcher(self.pto, st_bounds, temp_file.file_name, self.i, self.running, pprefix=self.pprefix) stitcher.enblend_lock = self.enblend_lock stitcher.nona_args = self.nona_args stitcher.enblend_args = self.enblend_args if self.dry: print 'dry: skipping partial stitch' stitcher = None else: stitcher.run() print print 'phase 3: loading supertile image' if self.dry: print 'dry: skipping loading PTO' img_fn = None else: if self.st_dir: self.st_fns.put(dst) #shutil.copyfile(temp_file.file_name, dst) args = [ 'convert', '-quality', '90', temp_file.file_name, dst ] print 'going to execute: %s' % (args, ) subp = subprocess.Popen(args, stdout=None, stderr=None, shell=False) subp.communicate() if subp.returncode != 0: raise Exception('Failed to copy stitched file') # having some problems that looks like file isn't getting written to disk # monitoring for such errors # remove if I can root cause the source of these glitches for i in xrange(30): if os.path.exists(dst): break if i == 0: print 'WARNING: soften missing strong blur dest file name %s, waiting a bit...' % ( dst, ) time.sleep(0.1) else: raise Exception( 'Missing soften strong blur output file name %s' % dst) # FIXME: was passing loaded image object # Directory should delete on exit # otherwise parent can delete it #img = PImage.from_file(temp_file.file_name) img_fn = temp_file.file_name # prevent deletion temp_file.file_name = '' #print 'supertile width: %d, height: %d' % (img.width(), img.height()) print 'Supertile done w/ fn %s' % (img_fn, ) return img_fn except: print 'supertile failed at %s' % (bench, ) raise
def try_supertile(self, x0, x1, y0, y1): '''x0/1 and y0/1 are global absolute coordinates''' # First generate all of the valid tiles across this area to see if we can get any useful work done? # every supertile should have at least one solution or the bounds aren't good bench = Benchmark() try: temp_file = ManagedTempFile.get(None, '.tif') bounds = [x0, x1, y0, y1] #out_name_base = "%s/r%03d_c%03d" % (self.out_dir, row, col) #print 'Working on %s' % out_name_base stitcher = PartialStitcher(self.pto, bounds, temp_file.file_name) if self.dry: print 'Dry: skipping partial stitch' stitcher = None else: stitcher.run() print print 'Phase 3: loading supertile image' if self.dry: print 'Dry: skipping loading PTO' img = None else: img = PImage.from_file(temp_file.file_name) print 'Supertile width: %d, height: %d' % (img.width(), img.height()) new = 0 ''' A tile is valid if its in a safe location There are two ways for the location to be safe: -No neighboring tiles as found on canvas edges -Sufficiently inside the blend area that artifacts should be minimal ''' gen_tiles = 0 print # TODO: get the old info back if I miss it after yield refactor print 'Phase 4: chopping up supertile' self.msg('step(x: %d, y: %d)' % (self.tw, self.th), 3) #self.msg('x in xrange(%d, %d, %d)' % (xt0, xt1, self.tw), 3) #self.msg('y in xrange(%d, %d, %d)' % (yt0, yt1, self.th), 3) for (y, x) in self.gen_supertile_tiles(x0, x1, y0, y1): # If we made it this far the tile can be constructed with acceptable enblend artifacts row = self.y2row(y) col = self.x2col(x) # Did we already do this tile? if self.is_done(row, col): # No use repeating it although it would be good to diff some of these print 'Rejecting tile x%d, y%d / r%d, c%d: already done' % (x, y, row, col) continue # note that x and y are in whole pano coords # we need to adjust to our frame # row and col on the other hand are used for global naming self.make_tile(img, x - x0, y - y0, row, col) gen_tiles += 1 bench.stop() print 'Generated %d new tiles for a total of %d / %d in %s' % (gen_tiles, len(self.closed_list), self.net_expected_tiles, str(bench)) if gen_tiles == 0: raise Exception("Didn't generate any tiles") # temp_file should be automatically deleted upon exit except: print 'Supertile failed at %s' % bench raise
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/pr0ntools_* 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 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("--prealigned") args.append("--fullscale") args.append("--minmatches") args.append("1") args.append("--ransacdist") args.append("5") args.append("--kdtreeseconddist") args.append("0.5") # 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) # Now run geocpset args = list() # output file args.append("-o") args.append(project.file_name) # input file args.append(project.file_name) (rc, output) = exc_ret_istr('geocpset', args, print_out=self.print_output) print 'PanoCP: geocpset 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.split('/')[-1]] 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 generate_core(self, image_file_names): command = "autopanoaj" args = list() final_project_file = ManagedTempFile.get(None, ".pto") temp_dir = ManagedTempDir.get() # default is .oto args.append("/project:hugin") # Use image args instead of dir # Images image_links = dict() for image_file_name in image_file_names: # args.append(image_file_name.replace("/tmp/", "Z:\\tmp\\")) image_file_name = os.path.realpath(image_file_name) link_file_name = os.path.join(temp_dir.file_name, os.path.basename(image_file_name)) print 'Linking %s -> %s' % (link_file_name, image_file_name) os.symlink(image_file_name, link_file_name) #sys.exit(1) # go go go (rc, output) = Execute.with_output(command, args, temp_dir.file_name) print 'Finished control point pair execution' if not rc == 0: print print print print 'output:\n%s' % output if output.find('This application has requested the Runtime to terminate it in an unusual way'): print 'WARNING: skipping crash' return None raise Exception('Bad rc: %d' % rc) ''' Doesn't like the match: PICTURE PAIRS VALIDATION Pair ( 0, 1) Ransac (In : 21, Out : 4, Residu : 4.43799) REMOVED Timing : 583.7 us ''' if output.find('REMOVED') >= 0: print 'WARNING: RANSAC invalidated control points' return None output_file_name = os.path.join(temp_dir.file_name, "panorama0.pto") # This happens occassionally, not sure why if not os.path.exists(output_file_name): print 'WARNING: missing output pto file!' return None # We return PTO object, not string # Ditch the gen file because its unreliable shutil.move(output_file_name, final_project_file.file_name) f = open(final_project_file.file_name, 'r') project_text = f.read() # Under WINE, do fixup # #-imgfile 2816 704 "Z:\tmp\pr0ntools_471477ADA1679A2E\pr0ntools_3CD1C0B1BB218E40.jpg" project_text = project_text.replace('Z:\\', '/').replace('\\', '/') for image_file_name in image_file_names: link_file_name = os.path.join(temp_dir.file_name, os.path.basename(image_file_name)) print 'Replacing %s -> %s' % (link_file_name, image_file_name) project_text = project_text.replace(link_file_name, image_file_name) if False: print print 'Raw control point project (after symbolic link and WINE file name substitution)' print print print project_text print print print #sys.exit(1) f.close() f = open(final_project_file.file_name, 'w') f.write(project_text) project = PTOProject.from_temp_file(final_project_file) return project
def control_points_by_subimage(self, pair, pair_images): ''' Just work on the overlap section, maybe even less ''' overlap = 1.0 / 3.0 images = [PImage.from_file(image_file_name) for image_file_name in pair_images] ''' 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() # 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() * (1.0 - overlap)) sub_image_1_x_end = int(images[1].width() * 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() * (1.0 - overlap)) sub_image_1_y_end = int(images[1].height() * 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_0_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_pair_images = (sub_image_0_file.file_name, sub_image_1_file.file_name) # image index to subimage file name link (not symbolic link) index_to_sub_file_name = dict() imgfile_index = 0 # 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] = pair_images[0] sub_to_real[sub_image_1_file.file_name] = pair_images[1] ''' # Hugin project file # generated by Autopano # Panorama settings: p w8000 h1200 f2 v250 n"PSD_mask" # input images: #-imgfile 2816 704 "/tmp/pr0ntools_C21F246F52E9D691/AA9627DC60B39FC8.jpg" o f0 y+0.000000 r+0.000000 p+0.000000 u20 d0.000000 e0.000000 v70.000000 a0.000000 b0.000000 c0.000000 #-imgfile 2816 704 "/tmp/pr0ntools_C21F246F52E9D691/EDE10C14171B2078.jpg" o f0 y+0.000000 r+0.000000 p+0.000000 u20 d0.000000 e0.000000 v70.000000 a0.000000 b0.000000 c0.000000 # Control points: c n0 N1 x1024 y176 X555 Y119 # Control Point No 0: 1.00000 c n0 N1 x1047 y160 X578 Y105 ... autopano-sift-c style file # Hugin project file generated by APSCpp p f2 w3000 h1500 v360 n"JPEG q90" m g1 i0 i w2816 h704 f0 a0 b-0.01 c0 d0 e0 p0 r0 v180 y0 u10 n"/tmp/pr0ntools_6691335AD228382E.jpg" i w2816 h938 f0 a0 b-0.01 c0 d0 e0 p0 r0 v180 y0 u10 n"/tmp/pr0ntools_64D97FF4621BC36E.jpg" v p1 r1 y1 # automatically generated control points c n0 N1 x1142.261719 y245.074757 X699.189408 Y426.042661 t0 c n0 N1 x887.417450 y164.602097 X1952.346197 Y921.975829 t0 ... c n0 N1 x823.803714 y130.802771 X674.596763 Y335.994699 t0 c n0 N1 x1097.192159 y121.170416 X937.394996 Y329.998934 t0 # :-) ''' fast_pair_project = self.control_point_gen.generate_core(sub_pair_images) if fast_pair_project is None: print 'WARNING: failed to gen control points @ %s' % repr(pair) return None out = '' part_pair_index = 0 for line in fast_pair_project.__repr__().split('\n'): if len(line) == 0: new_line = '' # This type of line is gen by autopano-sift-c elif line[0] == 'c': # c n0 N1 x1142.261719 y245.074757 X699.189408 Y426.042661 t0 ''' Okay def alphabetical issues # Not strictly related to this code, but close enough if not index_to_sub_file_name[0] == sub_image_0_file: print '0 index indicated file: %s, pair gen order expected %s' % (index_to_sub_file_name[0], sub_image_0_file) raise Exception('mismatch') if not index_to_sub_file_name[1] == sub_image_1_file: print '1 index indicated file: %s, pair gen order expected %s' % (index_to_sub_file_name[1], sub_image_1_file) raise Exception('mismatch') ''' # Parse parts = line.split() if not parts[1] == 'n0': print parts[1] raise Exception('mismatch') if not parts[2] == 'N1': print parts[2] raise Exception('mismatch') x = float(parts[3][1:]) y = float(parts[4][1:]) X = float(parts[5][1:]) Y = float(parts[6][1:]) #sub_image_1_x_end = image_1.width() #sub_image_1_y_end = image_1.height() # Adjust the image towards the upper left hand corner if index_to_sub_file_name[0] == sub_image_0_file.file_name: # normal adjustment x += sub_image_0_x_delta y += sub_image_0_y_delta elif index_to_sub_file_name[1] == sub_image_0_file.file_name: # they got flipped X += sub_image_0_x_delta Y += sub_image_0_y_delta else: print index_to_sub_file_name print 'index_to_sub_file_name[0]: %s' % repr(index_to_sub_file_name[0]) print 'index_to_sub_file_name[1]: %s' % repr(index_to_sub_file_name[1]) print 'sub_image_0_file: %s' % repr(sub_image_0_file) print 'sub_image_1_file: %s' % repr(sub_image_1_file) raise Exception("confused") # Write new_line = "c n0 N1 x%f y%f X%f Y%f t0" % (x, y, X, Y) out += new_line + '\n' # This type of line is generated by pto_merge elif line[0] == 'i': # i w2816 h704 f0 a0 b-0.01 c0 d0 e0 p0 r0 v180 y0 u10 n"/tmp/pr0ntools_6691335AD228382E.jpg" new_line = '' for part in line.split(): t = part[0] if t == 'i': new_line += 'i' elif t == 'w': new_line += ' w%d' % images[0].width() elif t == 'h': new_line += ' w%d' % images[0].height() elif t == 'n': new_line += ' n%s' % pair_images[part_pair_index] part_pair_index += 1 else: new_line += ' %s' % part print 'new line: %s' % new_line # These lines are generated by autopanoaj # The comment line is literally part of the file format, some sort of bizarre encoding # #-imgfile 2816 704 "/tmp/pr0ntools_2D24DE9F6CC513E0/pr0ntools_6575AA69EA66B3C3.jpg" # o f0 y+0.000000 r+0.000000 p+0.000000 u20 d0.000000 e0.000000 v70.000000 a0.000000 b0.000000 c0.000000 elif line.find('#-imgfile') == 0: # Replace pseudo file names with real ones new_line = line index_to_sub_file_name[imgfile_index] = line.split('"')[1] imgfile_index += 1 else: new_line = line out += new_line + '\n' else: out += line + '\n' for k in sub_to_real: v = sub_to_real[k] print 'Replacing %s => %s' % (k, v) out = out.replace(k, v) final_pair_project = PTOProject.from_text(out) return final_pair_project
def try_supertile(self, st_bounds): '''x0/1 and y0/1 are global absolute coordinates''' # First generate all of the valid tiles across this area to see if we can get any useful work done? # every supertile should have at least one solution or the bounds aren't good x0, x1, y0, y1 = st_bounds bench = Benchmark() try: if self.st_dir: # nah...tiff takes up too much space dst = os.path.join(self.st_dir, 'st_%06dx_%06dy.jpg' % (x0, y0)) if os.path.exists(dst): # normally this is a .tif so slight loss in quality print 'supertile short circuit on already existing: %s' % (dst,) return dst # st_081357x_000587y.jpg temp_file = ManagedTempFile.get(None, '.tif', prefix_mangle='st_%06dx_%06dy_' % (x0, y0)) stitcher = PartialStitcher(self.pto, st_bounds, temp_file.file_name, self.i, self.running, pprefix=self.pprefix) stitcher.enblend_lock = self.enblend_lock stitcher.nona_args = self.nona_args stitcher.enblend_args = self.enblend_args if self.dry: print 'dry: skipping partial stitch' stitcher = None else: stitcher.run() print print 'phase 3: loading supertile image' if self.dry: print 'dry: skipping loading PTO' img_fn = None else: if self.st_dir: self.st_fns.put(dst) #shutil.copyfile(temp_file.file_name, dst) args = ['convert', '-quality', '90', temp_file.file_name, dst] print 'going to execute: %s' % (args,) subp = subprocess.Popen(args, stdout=None, stderr=None, shell=False) subp.communicate() if subp.returncode != 0: raise Exception('Failed to copy stitched file') # having some problems that looks like file isn't getting written to disk # monitoring for such errors # remove if I can root cause the source of these glitches for i in xrange(30): if os.path.exists(dst): break if i == 0: print 'WARNING: soften missing strong blur dest file name %s, waiting a bit...' % (dst,) time.sleep(0.1) else: raise Exception('Missing soften strong blur output file name %s' % dst) # FIXME: was passing loaded image object # Directory should delete on exit # otherwise parent can delete it #img = PImage.from_file(temp_file.file_name) img_fn = temp_file.file_name # prevent deletion temp_file.file_name = '' #print 'supertile width: %d, height: %d' % (img.width(), img.height()) print 'Supertile done w/ fn %s' % (img_fn,) return img_fn except: print 'supertile failed at %s' % (bench,) raise
def control_points_by_subimage(self, pair, image_fn_pair, subimage_factor = None): '''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() if subimage_factor: y_overlap = subimage_factor[0] x_overlap = subimage_factor[1] else: 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() * (1.0 - x_overlap)) sub_image_1_x_end = int(images[1].width() * 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() * (1.0 - y_overlap)) sub_image_1_y_end = int(images[1].height() * 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_0_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 fast_pair_project = self.control_point_gen.generate_core(sub_image_fn_pair) if fast_pair_project is None: print 'WARNING: failed to gen control points @ %s' % repr(pair) return None oto_text = str(fast_pair_project) if 0: print oto_text # are we actually doing anything useful here? # The original intention was to make dead sure we had the right file order # but I'm pretty sure its consistent and we don't need to parse the comments final_pair_project = ajpto2pto_text(oto_text, 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 control_points_by_subimage(self, pair, image_fn_pair, subimage_factor=None): '''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() if subimage_factor: y_overlap = subimage_factor[0] x_overlap = subimage_factor[1] else: 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() * (1.0 - x_overlap)) sub_image_1_x_end = int(images[1].width() * 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() * (1.0 - y_overlap)) sub_image_1_y_end = int(images[1].height() * 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_0_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
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) (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 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