Пример #1
0
    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)
Пример #2
0
 def from_file_name(file_name, is_temporary=False):
     ret = PTOProject()
     ret.file_name = file_name
     if is_temporary:
         ret.temp_file = ManagedTempFile.from_existing(file_name)
     ret.parse()
     return ret
Пример #3
0
    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
Пример #4
0
    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)
Пример #5
0
    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)
Пример #6
0
def soften_composite(src_fn, dst_fn=None):
    tmp_file = ManagedTempFile.from_same_extension(src_fn)
    soften_gauss(src_fn, tmp_file.file_name)

    if dst_fn is None:
        dst_fn = src_fn

    args = ["convert"]
    args.append(src_fn)
    args.append(tmp_file.file_name)
    args.append("-compose")
    args.append("Blend")
    args.append("-define")
    args.append("compose:args=60,40%")
    args.append("-composite")
    # If we got a dest file, use it
    args.append(dst_fn)
    print('going to execute: %s' % (args, ))
    subp = subprocess.Popen(args, stdout=None, stderr=None, shell=False)
    subp.communicate()
    print('Execute done, rc: %s' % (subp.returncode, ))
    if not subp.returncode == 0:
        raise Exception('failed to form strong blur')

    # 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 range(30):
        if os.path.exists(dst_fn):
            break
        if i == 0:
            print(
                'WARNING: soften missing strong blur dest file name %s, waiting a bit...'
                % (dst_fn, ))
        time.sleep(0.1)
    else:
        raise Exception('Missing soften strong blur output file name %s' %
                        dst_fn)
Пример #7
0
    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()
Пример #8
0
    def do_generate_control_points_by_pair(self, pair, image_fn_pair):
        '''high level function uses by sub-stitches.  Given a pair of images make a best effort to return a .pto object'''
        '''
        pair: ImageCoordinatePair() object
        image_fn_pair: tuple of strings

        Algorithm:
        First try to stitch normally (either whole image or partial depending on the mode)
        If that doesn't succeed and softening is enabled try up to three times to soften to produce a match
        If that still doesn't produce a decent solution return None and let higher levels deal with
        '''
        soften_iterations = 3

        print
        print
        #print 'Generating project for image pair (%s / %s, %s / %s)' % (image_fn_pair[0], str(pair[0]), image_fn_pair[1], str(pair[1]))
        print 'Generating project for image pair (%s, %s)' % (image_fn_pair[0],
                                                              image_fn_pair[1])

        if True:
            # Try raw initially
            print 'Attempting sharp match...'
            ret_project = self.try_control_points_with_position(
                pair, image_fn_pair)
            if ret_project:
                return ret_project

        print 'WARNING: bad project, attempting soften...'

        soften_image_file_0_managed = ManagedTempFile.from_same_extension(
            image_fn_pair[0])
        soften_image_file_1_managed = ManagedTempFile.from_same_extension(
            image_fn_pair[1])
        print 'Soften fn0: %s' % soften_image_file_0_managed.file_name
        print 'Soften fn1: %s' % soften_image_file_1_managed.file_name

        for i in xrange(soften_iterations):
            self.soften_try[i] += 1

            # And then start screwing with it
            # Wonder if we can combine features from multiple soften passes?
            # Or at least take the maximum
            # Do features get much less accurate as the soften gets up there?

            print 'Attempting soften %d / %d' % (i + 1, soften_iterations)

            if i == 0:
                soften_composite(image_fn_pair[0],
                                 soften_image_file_0_managed.file_name)
                soften_composite(image_fn_pair[1],
                                 soften_image_file_1_managed.file_name)
            else:
                soften_composite(soften_image_file_0_managed.file_name)
                soften_composite(soften_image_file_1_managed.file_name)

            pair_soften_image_file_names = (
                soften_image_file_0_managed.file_name,
                soften_image_file_1_managed.file_name)
            ret_project = self.try_control_points_with_position(
                pair, pair_soften_image_file_names)
            # Did we win?
            if ret_project:
                # Fixup the project to reflect the correct file names
                text = str(ret_project)
                if 0:
                    print
                    print 'Before sub'
                    print
                    print str(ret_project)
                    print
                    print
                    print
                print '%s => %s' % (soften_image_file_0_managed.file_name,
                                    image_fn_pair[0])
                text = text.replace(soften_image_file_0_managed.file_name,
                                    image_fn_pair[0])
                print '%s => %s' % (soften_image_file_1_managed.file_name,
                                    image_fn_pair[1])
                text = text.replace(soften_image_file_1_managed.file_name,
                                    image_fn_pair[1])

                ret_project.set_text(text)
                if 0:
                    print
                    print 'After sub'
                    print
                    print str(ret_project)
                    print
                    print
                    print
                    #sys.exit(1)
                self.soften_ok[i] += 1
                print 'Soften try: %s' % (self.soften_try, )
                print 'Soften ok: %s' % (self.soften_ok, )
                return ret_project

        print 'WARNING: gave up on generating control points!'
        return None
Пример #9
0
    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
Пример #10
0
    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