def chainfunc(self, pad, buffer): try: print 'Got resize buffer' # Simplest: just propagate the data # self.srcpad.push(buffer) # Import into PIL and downsize it # Raw jpeg to pr0n PIL wrapper object print 'resize chain', len(buffer.data), len(buffer.data) / 3264.0 #open('temp.jpg', 'w').write(buffer.data) #io = StringIO.StringIO(buffer.data) io = StringIO.StringIO(str(buffer)) try: image = PImage.from_image(Image.open(io)) except: print 'failed to create image' return gst.FLOW_OK # Use a fast filter since this is realtime image = image.get_scaled(0.5, Image.NEAREST) output = StringIO.StringIO() image.save(output, 'jpeg') self.srcpad.push(gst.Buffer(output.getvalue())) except: traceback.print_exc() os._exit(1) return gst.FLOW_OK
def __init__(self, image_in, threads=1): self.image_in = image_in self.pim = PImage.from_file(self.image_in) self.im_ext = ".jpg" self.threads = threads self.tw = 250 self.th = 250
def verify_layer_sizes(self, images): width = None height = None reference_layer = None print images # Image can't read SVG # We could read the width/height attr still though for a_image in images: if a_image.find('.svg') < 0: raise Exception('Require .svg files, got %s' % a_image ) return for image_name in images: this_image = PImage.from_file(image_name) this_width = this_image.width() this_height = this_image.width() if width == None: width = this_width height = this_height reference_image = this_image continue if this_width != width or this_height != height: print '%s size (width=%u, height=%u) does not match %s size (width=%u, height=%u)' % \ (reference_image.file_name(), width, height, \ this_image.file_name(), this_width, this_height) raise Exception("Image size mismatch")
def __init__(self, image_in, threads=1): self.image_in = image_in self.pim = PImage.from_file(self.image_in) self.im_ext = '.jpg' self.threads = threads self.tw = 250 self.th = 250
def add_point(self, y, x, image_file_name): image = PImage.from_file(image_file_name) point = SpatialPoint(image_file_name, y, x, image.height(), image.width()) if image_file_name in self.points: raise Exception("duplicate key") self.points[image_file_name] = point #print self.x_list #print self.y_list self.y_list.add(point) self.x_list.add(point) self.is_sorted = False
def captureSnapshot(self, image_id): print 'RX image for saving' image = PImage.from_image(self.capture_sink.pop_image(image_id)) fn_full = os.path.join(config['imager']['snapshot_dir'], str(self.snapshot_fn_le.text())) factor = float(config['imager']['scalar']) # Use a reasonably high quality filter image.get_scaled(factor, Image.ANTIALIAS).save(fn_full) # That image is done, get read for the next self.snapshot_next_serial() self.snapshot_pb.setEnabled(True)
def get_file_names(file_names_in, depth): file_names = list() first_parts = set() second_parts = set() for file_name_in in file_names_in: if os.path.isfile(file_name_in): if PImage.is_image_filename(file_name_in): file_names.append(file_name_in) elif os.path.isdir(file_name_in): if depth: for file_name in os.listdir(file_name_in): file_names.append(get_file_names(os.path.join(file_name_in, file_name), depth - 1)) return file_names
def run(self): fn = self.fn max_level = self.max_level min_level = self.min_level out_dir_base = self.out_dir_base if min_level is None: min_level = 0 i = PImage.from_file(fn) if max_level is None: max_level = calc_max_level_from_image(i) t_width = 250 t_height = 250 ''' Expect that will not need images larger than 1 terapixel in the near future sqrt(1 T / (256 * 256)) = 3906, in hex = 0xF42 so three hex digits should last some time ''' if out_dir_base is None: out_dir_base = 'tiles_out/' ''' Test file is the carved out metal sample of the 6522 It is 5672 x 4373 pixels I might do a smaller one first ''' if os.path.exists(out_dir_base): os.system('rm -rf %s' % out_dir_base) os.mkdir(out_dir_base) for zoom_level in xrange(max_level, min_level - 1, -1): print print '************' print 'Zoom level %d' % zoom_level out_dir = '%s/%d' % (out_dir_base, zoom_level) if os.path.exists(out_dir): os.system('rm -rf %s' % out_dir) os.mkdir(out_dir) tiler = ImageTiler(i) tiler.out_dir = out_dir tiler.run() if zoom_level != min_level: # Each zoom level is half smaller than previous i = i.get_scaled(0.5, filt=Image.ANTIALIAS) if 0: i.save('test.jpg') sys.exit(1)
def get_file_names(file_names_in, depth): file_names = list() first_parts = set() second_parts = set() for file_name_in in file_names_in: if os.path.isfile(file_name_in): if PImage.is_image_filename(file_name_in): file_names.append(file_name_in) elif os.path.isdir(file_name_in): if depth: for file_name in os.listdir(file_name_in): file_names.append( get_file_names( os.path.join(file_name_in, file_name), depth - 1)) return file_names
def take_picture(self, file_name_out = None): print 'gstreamer imager: taking image to %s' % file_name_out def emitSnapshotCaptured(image_id): print 'Image captured reported: %s' % image_id self.image_id = image_id self.image_ready.set() self.image_id = None self.image_ready.clear() self.gui.capture_sink.request_image(emitSnapshotCaptured) print 'Waiting for next image...' self.image_ready.wait() print 'Got image %s' % self.image_id image = PImage.from_image(self.gui.capture_sink.pop_image(self.image_id)) factor = float(config['imager']['scalar']) # Use a reasonably high quality filter scaled = image.get_scaled(factor, Image.ANTIALIAS) if not self.gui.dry(): scaled.save(file_name_out)
def fix_il(il): v = il.get_variable('v') if v == None or v >= 180: il.set_variable('v', 51) # These aren't liked: TrX0 TrY0 TrZ0 il.remove_variable('TrX') il.remove_variable('TrY') il.remove_variable('TrZ') # panotools seems to set these to -1 on some ocassions if il.get_variable('w') == None or il.get_variable('h') == None or int(il.get_variable('w')) <= 0 or int(il.get_variable('h')) <= 0: img = PImage.from_file(il.get_name()) il.set_variable('w', img.width()) il.set_variable('h', img.height()) for v in 'd e'.split(): if il.get_variable(v) == None or reoptimize: il.set_variable(v, 0)
def fix_il(il): v = il.get_variable('v') if v == None or v >= 180: il.set_variable('v', 51) # These aren't liked: TrX0 TrY0 TrZ0 il.remove_variable('TrX') il.remove_variable('TrY') il.remove_variable('TrZ') # panotools seems to set these to -1 on some ocassions if il.get_variable('w') == None or il.get_variable('h') == None or int(il.get_variable('w')) <= 0 or int(il.get_variable('h')) <= 0: img = PImage.from_file(il.get_name()) il.set_variable('w', img.width()) il.set_variable('h', img.height()) # Force reoptimize by zeroing optimization result il.set_variable('d', 0) il.set_variable('e', 0)
def fix_il(il): v = il.get_variable('v') if v == None or v >= 180: il.set_variable('v', 51) # These aren't liked: TrX0 TrY0 TrZ0 il.remove_variable('TrX') il.remove_variable('TrY') il.remove_variable('TrZ') # panotools seems to set these to -1 on some ocassions if il.get_variable('w') == None or il.get_variable('h') == None or int( il.get_variable('w')) <= 0 or int(il.get_variable('h')) <= 0: img = PImage.from_file(il.get_name()) il.set_variable('w', img.width()) il.set_variable('h', img.height()) for v in 'd e'.split(): if il.get_variable(v) == None or reoptimize: il.set_variable(v, 0)
def fix_il(il): v = il.get_variable('v') if v == None or v >= 180: il.set_variable('v', 51) # These aren't liked: TrX0 TrY0 TrZ0 il.remove_variable('TrX') il.remove_variable('TrY') il.remove_variable('TrZ') # panotools seems to set these to -1 on some ocassions if il.get_variable('w') == None or il.get_variable( 'h') == None or int(il.get_variable('w')) <= 0 or int( il.get_variable('h')) <= 0: img = PImage.from_file(il.get_name()) il.set_variable('w', img.width()) il.set_variable('h', img.height()) # Force reoptimize by zeroing optimization result il.set_variable('d', 0) il.set_variable('e', 0)
def fix_il(il): v = il.get_variable('v') if v == None or v >= 180: il.set_variable('v', 51) # panotools seems to set these to -1 on some ocassions if il.get_variable('w') == None or il.get_variable('h') == None or int(il.get_variable('w')) <= 0 or int(il.get_variable('h')) <= 0: img = PImage.from_file(il.get_name()) il.set_variable('w', img.width()) il.set_variable('h', img.height()) for v in 'd e'.split(): if il.get_variable(v) == None or reoptimize: il.set_variable(v, 0) #print 'setting var' nv = {} for k, v in il.variables.iteritems(): if k in ['w', 'h', 'f', 'Va', 'Vb', 'Vc', 'Vd', 'Vx', 'Vy', 'd', 'e', 'g', 't', 'v', 'Vm', 'n']: nv[k] = v il.variables = nv
def get_image(self): if self.image is None: self.image = PImage.from_file(self.get_name()) return self.image
def run(self): print 'Input images width %d, height %d' % (self.img_width, self.img_height) print 'Output to %s' % self.out_dir print 'Super tile width %d, height %d from scalar %d' % (self.stw, self.sth, self.st_scalar_heuristic) print 'Super tile x step %d, y step %d' % (self.super_t_xstep, self.super_t_ystep) print 'Supertile clip width %d, height %d' % (self.clip_width, self.clip_height) if self.merge and self.force: raise Exception('Can not merge and force') if not self.dry: self.dry = True print print print print '***BEGIN DRY RUN***' self.run() print '***END DRY RUN***' print print print self.dry = False if not self.ignore_crop and self.pto.get_panorama_line().getv('S') is None: raise Exception('Not cropped. Set ignore crop to force continue') ''' if we have a width of 256 and 1 pixel we need total size of 256 If we have a width of 256 and 256 pixels we need total size of 256 if we have a width of 256 and 257 pixel we need total size of 512 ''' print 'Tile width: %d, height: %d' % (self.tw, self.th) print 'Net size: %d width (%d:%d) X %d height (%d:%d) = %d MP' % (self.width(), self.left(), self.right(), self.height(), self.top(), self.bottom(), self.width() * self.height() / 1000000) print 'Output image extension: %s' % self.out_extension self.this_tiles_done = 0 bench = Benchmark() # Scrub old dir if we don't want it if os.path.exists(self.out_dir) and not self.merge: if not self.force: raise Exception("Must set force to override output") if not self.dry: shutil.rmtree(self.out_dir) if not self.dry and not os.path.exists(self.out_dir): os.mkdir(self.out_dir) if self.st_dir and not self.dry and not os.path.exists(self.st_dir): os.mkdir(self.st_dir) # in form (row, col) self.closed_list = set() self.n_expected_sts = len(list(self.gen_supertiles())) print 'M: Generating %d supertiles' % self.n_expected_sts x_tiles_ideal = 1.0 * self.width() / self.tw x_tiles = math.ceil(x_tiles_ideal) y_tiles_ideal = 1.0 * self.height() / self.th y_tiles = math.ceil(y_tiles_ideal) self.net_expected_tiles = x_tiles * y_tiles ideal_tiles = x_tiles_ideal * y_tiles_ideal print 'M: Ideal tiles: %0.3f x, %0.3f y tiles => %0.3f net' % ( x_tiles_ideal, y_tiles_ideal, ideal_tiles) print 'M: Expecting to generate x%d, y%d => %d basic tiles' % ( x_tiles, y_tiles, self.net_expected_tiles) if self.merge: self.seed_merge() if self.is_full: print 'M: full => forcing 1 thread ' self.threads = 1 print 'M: Initializing %d workers' % self.threads self.workers = [] for ti in xrange(self.threads): print 'Bringing up W%02d' % ti w = Worker(ti, self, os.path.join(self.log_dir, 'w%02d.log' % ti)) self.workers.append(w) w.start() print print print print 'S' * 80 print 'M: Serial end' print 'P' * 80 try: #temp_file = 'partial.tif' self.n_supertiles = 0 st_gen = self.gen_supertiles() all_allocated = False last_progress = time.time() pair_submit = 0 pair_complete = 0 idle = False while not (all_allocated and pair_complete == pair_submit): progress = False # Check for completed jobs for wi, worker in enumerate(self.workers): try: out = worker.qo.get(False) except Queue.Empty: continue pair_complete += 1 what = out[0] progress = True if what == 'done': (st_bounds, img_fn) = out[1] print 'MW%d: done w/ submit %d, complete %d' % (wi, pair_submit, pair_complete) # Dry run if img_fn is None: pim = None else: pim = PImage.from_file(img_fn) # hack # ugh remove may be an already existing supertile (not a temp file) #os.remove(img_fn) self.process_image(pim, st_bounds) elif what == 'exception': if not self.ignore_errors: for worker in self.workers: worker.running.clear() # let stdout clear up # (only moderately effective) time.sleep(1) #(_task, e) = out[1] print '!' * 80 print 'M: ERROR: MW%d failed w/ exception' % wi (_task, _e, estr) = out[1] print 'M: Stack trace:' for l in estr.split('\n'): print l print '!' * 80 if not self.ignore_errors: raise Exception('M: shutdown on worker failure') print 'M WARNING: continuing despite worker failure' else: print 'M: %s' % (out,) raise Exception('M: internal error: bad task type %s' % what) self.st_limit -= 1 if self.st_limit == 0: print 'Breaking on ST limit reached' break # Any workers need more work? for wi, worker in enumerate(self.workers): if all_allocated: break if worker.qi.empty(): while True: try: st_bounds = st_gen.next() except StopIteration: print 'M: all tasks allocated' all_allocated = True break progress = True [x0, x1, y0, y1] = st_bounds self.n_supertiles += 1 print 'M: checking supertile x(%d:%d) y(%d:%d)' % (x0, x1, y0, y1) if not self.should_try_supertile(st_bounds): print 'M WARNING: skipping supertile %d as it would not generate any new tiles' % self.n_supertiles continue print '*' * 80 #print 'W%d: submit %s (%d / %d)' % (wi, repr(pair), pair_submit, n_pairs) print "Creating supertile %d / %d with x%d:%d, y%d:%d" % (self.n_supertiles, self.n_expected_sts, x0, x1, y0, y1) print 'W%d: submit' % (wi,) worker.qi.put((st_bounds,)) pair_submit += 1 break if progress: last_progress = time.time() idle = False else: if not idle: print 'M Server thread idle' idle = True # can take some time, but should be using smaller tiles now if time.time() - last_progress > 4 * 60 * 60: print 'M WARNING: server thread stalled' last_progress = time.time() time.sleep(0.1) bench.stop() print 'M Processed %d supertiles to generate %d new (%d total) tiles in %s' % (self.n_expected_sts, self.this_tiles_done, self.tiles_done(), str(bench)) tiles_s = self.this_tiles_done / bench.delta_s() print 'M %f tiles / sec, %f pix / sec' % (tiles_s, tiles_s * self.tw * self.th) if self.tiles_done() != self.net_expected_tiles: print 'M ERROR: expected to do %d basic tiles but did %d' % (self.net_expected_tiles, self.tiles_done()) self.dump_open_list() raise Exception('State mismatch') # Gather up supertile filenames generated by workers # xxx: maybe we should tell slaves the file they should use? for worker in self.workers: while True: try: st_fn = worker.st_fns.get(False) except Queue.Empty: break self.st_fns.append(st_fn) finally: self.wkill() self.workers = None
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()) return img_fn except: print 'supertile failed at %s' % (bench,) raise
def calc_il_dim(il): name = il.get_name() pimage = PImage.from_file(name) il.set_width(pimage.width()) il.set_height(pimage.height())
def rotate_tiles(src_dir, dst_dir, degrees, force=False, rc=False): self = Object() if src_dir[-1] == '/': src_dir = src_dir[0:-1] if dst_dir is None: dst_dir = src_dir + "-rotated" if os.path.exists(dst_dir): if force: shutil.rmtree(dst_dir) else: raise Exception('Output alrady exists, must set force') if not os.path.exists(dst_dir): os.mkdir(dst_dir) if degrees == 0: print 'WARNING: rotate got 0 degrees, aborting' return # And that only if the tiles are the same width and height # which is not required but the usual if not degrees in (90, 180, 270): #if not degrees in [180]: raise Exception('Only right angle degrees currently supported') print 'Rotating dir %s to dir %s %d degrees' % (src_dir, dst_dir, degrees) icm = ImageCoordinateMap.from_dir_tagged_file_names(src_dir) # Verify uniform size print "Verifying tile size...." self.tw = None self.th = None # For the first level we copy things over n = 0 for (src, row, col) in icm.images(): n += 1 pi = PImage.from_file(src) # I could actually set with / height here but right now this is # coming up fomr me accidentially using 256 x 256 tiles when the # standard is 250 x 250 if self.tw is None: self.tw = pi.width() if self.th is None: self.th = pi.height() if pi.width() != self.tw or pi.height() != self.th: raise Exception('Source image incorrect size') this_n = 0 for (src, src_row, src_col) in icm.images(): this_n += 1 extension = '.jpg' extension = '.' + src.split('.')[-1] if degrees == 180: dst_row = icm.height() - src_row - 1 dst_col = icm.width() - src_col - 1 elif degrees == 90: # r0-c0 => r0-cn # r0-cn => rn-cm dst_row = src_col dst_col = icm.height() - src_row - 1 elif degrees == 270: dst_row = icm.height() - src_col - 1 dst_col = src_row else: dst_row = src_row dst_col = src_col if rc: dst = os.path.join(dst_dir, 'c%04d_r%04d%s' % (dst_col, dst_row, extension)) else: dst = os.path.join(dst_dir, 'y%03d_x%03d%s' % (dst_row, dst_col, extension)) pi = PImage.from_file(src) # rotates CCW...w/e pip = pi.rotate(-degrees) print '%d / %d: %s => %s' % (this_n, n, src, dst) pip.save(dst)
arg_value_bool = True if arg.find("=") > 0: arg_key = arg.split("=")[0][2:] arg_value = arg.split("=")[1] if arg_value == "false" or arg_value == "0" or arg_value == "no": arg_value_bool = False else: arg_key = arg[2:] if arg_key == "help": help() sys.exit(0) elif arg_key == "result" or arg_key == "out": if arg_value.find('.pto') > 0: output_project_file_name = arg_value elif PImage.is_image_filename(arg_value): output_image_file_name = arg_value else: arg_fatal('unknown file type %s, use explicit version' % arg) elif arg_key == "grid-only": grid_only = arg_value_bool elif arg_key == "algorithm": algorithm = arg_value elif arg_key == "n-rows": n_rows = int(arg_value) elif arg_key == "n-cols": n_cols = int(arg_value) elif arg_key == "alt-rows": alt_rows = arg_value_bool elif arg_key == "alt-cols": alt_cols = arg_value_bool
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 __init__(self, image_in): self.image_in = image_in self.image = PImage.from_file(self.image_in) self.set_out_extension('.jpg')
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 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 run(self): print 'Input images width %d, height %d' % (self.img_width, self.img_height) print 'Output to %s' % self.out_dir print 'Super tile width %d, height %d from scalar %d' % ( self.stw, self.sth, self.st_scalar_heuristic) print 'Super tile x step %d, y step %d' % (self.super_t_xstep, self.super_t_ystep) print 'Supertile clip width %d, height %d' % (self.clip_width, self.clip_height) if self.merge and self.force: raise Exception('Can not merge and force') if not self.dry: self.dry = True print print print print '***BEGIN DRY RUN***' self.run() print '***END DRY RUN***' print print print self.dry = False if not self.ignore_crop and self.pto.get_panorama_line().getv( 'S') is None: raise Exception('Not cropped. Set ignore crop to force continue') ''' if we have a width of 256 and 1 pixel we need total size of 256 If we have a width of 256 and 256 pixels we need total size of 256 if we have a width of 256 and 257 pixel we need total size of 512 ''' print 'Tile width: %d, height: %d' % (self.tw, self.th) print 'Net size: %d width (%d:%d) X %d height (%d:%d) = %d MP' % ( self.width(), self.left(), self.right(), self.height(), self.top(), self.bottom(), self.width() * self.height() / 1000000) print 'Output image extension: %s' % self.out_extension self.this_tiles_done = 0 bench = Benchmark() # Scrub old dir if we don't want it if os.path.exists(self.out_dir) and not self.merge: if not self.force: raise Exception("Must set force to override output") if not self.dry: shutil.rmtree(self.out_dir) if not self.dry and not os.path.exists(self.out_dir): os.mkdir(self.out_dir) if self.st_dir and not self.dry and not os.path.exists(self.st_dir): os.mkdir(self.st_dir) # in form (row, col) self.closed_list = set() self.n_expected_sts = len(list(self.gen_supertiles())) print 'M: Generating %d supertiles' % self.n_expected_sts x_tiles_ideal = 1.0 * self.width() / self.tw x_tiles = math.ceil(x_tiles_ideal) y_tiles_ideal = 1.0 * self.height() / self.th y_tiles = math.ceil(y_tiles_ideal) self.net_expected_tiles = x_tiles * y_tiles ideal_tiles = x_tiles_ideal * y_tiles_ideal print 'M: Ideal tiles: %0.3f x, %0.3f y tiles => %0.3f net' % ( x_tiles_ideal, y_tiles_ideal, ideal_tiles) print 'M: Expecting to generate x%d, y%d => %d basic tiles' % ( x_tiles, y_tiles, self.net_expected_tiles) if self.merge: self.seed_merge() if self.is_full: print 'M: full => forcing 1 thread ' self.threads = 1 print 'M: Initializing %d workers' % self.threads self.workers = [] for ti in xrange(self.threads): print 'Bringing up W%02d' % ti w = Worker(ti, self, os.path.join(self.log_dir, 'w%02d.log' % ti)) self.workers.append(w) w.start() print print print print 'S' * 80 print 'M: Serial end' print 'P' * 80 try: #temp_file = 'partial.tif' self.n_supertiles = 0 st_gen = self.gen_supertiles() all_allocated = False last_progress = time.time() pair_submit = 0 pair_complete = 0 idle = False while not (all_allocated and pair_complete == pair_submit): progress = False # Check for completed jobs for wi, worker in enumerate(self.workers): try: out = worker.qo.get(False) except Queue.Empty: continue pair_complete += 1 what = out[0] progress = True if what == 'done': (st_bounds, img_fn) = out[1] print 'MW%d: done w/ submit %d, complete %d' % ( wi, pair_submit, pair_complete) # Dry run if img_fn is None: pim = None else: pim = PImage.from_file(img_fn) # hack # ugh remove may be an already existing supertile (not a temp file) #os.remove(img_fn) self.process_image(pim, st_bounds) elif what == 'exception': if not self.ignore_errors: for worker in self.workers: worker.running.clear() # let stdout clear up # (only moderately effective) time.sleep(1) #(_task, e) = out[1] print '!' * 80 print 'M: ERROR: MW%d failed w/ exception' % wi (_task, _e, estr) = out[1] print 'M: Stack trace:' for l in estr.split('\n'): print l print '!' * 80 if not self.ignore_errors: raise Exception('M: shutdown on worker failure') print 'M WARNING: continuing despite worker failure' else: print 'M: %s' % (out, ) raise Exception('M: internal error: bad task type %s' % what) self.st_limit -= 1 if self.st_limit == 0: print 'Breaking on ST limit reached' break # Any workers need more work? for wi, worker in enumerate(self.workers): if all_allocated: break if worker.qi.empty(): while True: try: st_bounds = st_gen.next() except StopIteration: print 'M: all tasks allocated' all_allocated = True break progress = True [x0, x1, y0, y1] = st_bounds self.n_supertiles += 1 print 'M: checking supertile x(%d:%d) y(%d:%d)' % ( x0, x1, y0, y1) if not self.should_try_supertile(st_bounds): print 'M WARNING: skipping supertile %d as it would not generate any new tiles' % self.n_supertiles continue print '*' * 80 #print 'W%d: submit %s (%d / %d)' % (wi, repr(pair), pair_submit, n_pairs) print "Creating supertile %d / %d with x%d:%d, y%d:%d" % ( self.n_supertiles, self.n_expected_sts, x0, x1, y0, y1) print 'W%d: submit' % (wi, ) worker.qi.put((st_bounds, )) pair_submit += 1 break if progress: last_progress = time.time() idle = False else: if not idle: print 'M Server thread idle' idle = True # can take some time, but should be using smaller tiles now if time.time() - last_progress > 4 * 60 * 60: print 'M WARNING: server thread stalled' last_progress = time.time() time.sleep(0.1) bench.stop() print 'M Processed %d supertiles to generate %d new (%d total) tiles in %s' % ( self.n_expected_sts, self.this_tiles_done, self.tiles_done(), str(bench)) tiles_s = self.this_tiles_done / bench.delta_s() print 'M %f tiles / sec, %f pix / sec' % (tiles_s, tiles_s * self.tw * self.th) if self.tiles_done() != self.net_expected_tiles: print 'M ERROR: expected to do %d basic tiles but did %d' % ( self.net_expected_tiles, self.tiles_done()) self.dump_open_list() raise Exception('State mismatch') # Gather up supertile filenames generated by workers # xxx: maybe we should tell slaves the file they should use? for worker in self.workers: while True: try: st_fn = worker.st_fns.get(False) except Queue.Empty: break self.st_fns.append(st_fn) finally: self.wkill() self.workers = None
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 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 rotate_tiles(src_dir, dst_dir, degrees, force = False, rc = False): self = Object() if src_dir[-1] == '/': src_dir = src_dir[0:-1] if dst_dir is None: dst_dir = src_dir + "-rotated" if os.path.exists(dst_dir): if force: shutil.rmtree(dst_dir) else: raise Exception('Output alrady exists, must set force') if not os.path.exists(dst_dir): os.mkdir(dst_dir) if degrees == 0: print 'WARNING: rotate got 0 degrees, aborting' return # And that only if the tiles are the same width and height # which is not required but the usual if not degrees in (90, 180, 270): #if not degrees in [180]: raise Exception('Only right angle degrees currently supported') print 'Rotating dir %s to dir %s %d degrees' % (src_dir, dst_dir, degrees) icm = ImageCoordinateMap.from_dir_tagged_file_names(src_dir) # Verify uniform size print "Verifying tile size...." self.tw = None self.th = None # For the first level we copy things over n = 0 for (src, row, col) in icm.images(): n += 1 pi = PImage.from_file(src) # I could actually set with / height here but right now this is # coming up fomr me accidentially using 256 x 256 tiles when the # standard is 250 x 250 if self.tw is None: self.tw = pi.width() if self.th is None: self.th = pi.height() if pi.width() != self.tw or pi.height() != self.th: raise Exception('Source image incorrect size') this_n = 0 for (src, src_row, src_col) in icm.images(): this_n += 1 extension = '.jpg' extension = '.' + src.split('.')[-1] if degrees == 180: dst_row = icm.height() - src_row - 1 dst_col = icm.width() - src_col - 1 elif degrees == 90: # r0-c0 => r0-cn # r0-cn => rn-cm dst_row = src_col dst_col = icm.height() - src_row - 1 elif degrees == 270: dst_row = icm.height() - src_col - 1 dst_col = src_row else: dst_row = src_row dst_col = src_col if rc: dst = os.path.join(dst_dir, 'c%04d_r%04d%s' % (dst_col, dst_row, extension)) else: dst = os.path.join(dst_dir, 'y%03d_x%03d%s' % (dst_row, dst_col, extension)) pi = PImage.from_file(src) # rotates CCW...w/e pip = pi.rotate(-degrees) print '%d / %d: %s => %s' % (this_n, n, src, dst) pip.save(dst)
def run(self): self.prep_out_dir_base() for self.zoom_level in xrange(self.max_level, self.min_level - 1, -1): print print '************' print 'Zoom level %d' % self.zoom_level out_dir = '%s/%d' % (self.out_dir_base, self.zoom_level) if os.path.exists(out_dir): os.system('rm -rf %s' % out_dir) os.mkdir(out_dir) # For the first level we copy things over if self.zoom_level == self.max_level: for (img_fn, row, col) in self.map.images(): dst = self.get_fn(row, col) if 0: print 'Direct copying %s => %s' % (img_fn, dst) shutil.copy(img_fn, dst) # This allows to do type conversions if needed # Presumably the conversion process for jps should be lossless although I haven't verified else: print 'Basic conversion %s => %s w/ quality %u' % (img_fn, dst, self.quality) pi = PImage.from_file(img_fn) # I could actually set with / height here but right now this is # coming up fomr me accidentially using 256 x 256 tiles when the # standard is 250 x 250 if self.t_width is None: self.t_width = pi.width() if self.t_height is None: self.t_height = pi.height() if pi.width() != self.t_width or pi.height() != self.t_height: raise Exception('Source image incorrect size') pi.save(dst, quality=self.quality) # Additional levels we take the image coordinate map and shrink else: # Prepare a new image coordinate map so we can form the next tile set new_cols = int(math.ceil(1.0 * self.map.width() / self.zoom_factor)) new_rows = int(math.ceil(1.0 * self.map.height() / self.zoom_factor)) #print 'Shrink by %s: cols %s => %s, rows %s => %s' % (str(self.zoom_factor), self.map.width(), new_cols, self.map.height(), new_rows) if 0: print self.map.debug_print() print new_map = ImageCoordinateMap(new_cols, new_rows) todo = new_rows * new_cols this = 0 for new_row in xrange(new_rows): old_row = new_row * self.zoom_factor for new_col in xrange(new_cols): this += 1 old_col = new_col * self.zoom_factor #print print 'z%d %d/%d: transforming row %d => %d, col %d => %d w/ quality %u' % (self.zoom_level, this, todo, old_row, new_row, old_col, new_col, self.quality) # Paste the old (4) images together imgp = PImage.from_filename_array([[self.get_old(old_row + 0, old_col + 0), self.get_old(old_row + 0, old_col + 1)], [self.get_old(old_row + 1, old_col + 0), self.get_old(old_row + 1, old_col + 1)]]) if imgp.width() != self.t_width * self.zoom_factor or imgp.height() != self.t_height * self.zoom_factor: print 'New image width %d, height: %d from tile width %d, height %d' % (imgp.width(), imgp.height(), self.t_width, self.t_height) raise Exception('Combined image incorrect size') scaled = imgp.get_scaled(0.5, filt=Image.ANTIALIAS) if scaled.width() != self.t_width or scaled.height() != self.t_height: raise Exception('Scaled image incorrect size') new_fn = self.get_fn(new_row, new_col) scaled.save(new_fn, quality=self.quality) #sys.exit(1) new_map.set_image(new_col, new_row, new_fn) # Next shrink will be on the previous tile set, not the original print 'Rotating image map' self.map = new_map
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