Example #1
0
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'
Example #2
0
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')
Example #3
0
    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
Example #4
0
    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'
Example #5
0
	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'
Example #6
0
	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
Example #7
0
    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
Example #8
0
    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)
Example #9
0
    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)
Example #10
0
	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'