def create_shape_image_sample(shape, sample_width=256, sample_height=256): """ Return a centered image sample of the shape, or None if a centered box intersects the shape edge """ from shapes.models import ShapeImageSample from common.wavelets import compute_wavelet_feature_vector if ShapeImageSample.objects.filter(shape=shape).exists(): return image = open_image(shape.photo.image_2048) image_width, image_height = image.size triangles = parse_triangles(shape.triangles) vertices = [(x * image_width, y * image_height) for (x, y) in parse_vertices(shape.vertices)] b = bbox_vertices(vertices) # make sure box is big enough if b[2] - b[0] < sample_width or b[3] - b[1] < sample_height: return None # try random boxes filled = False for iters in xrange(1000): x = int(random.uniform(b[0] + sample_width / 2, b[2] - sample_width / 2)) y = int(random.uniform(b[1] + sample_height / 2, b[3] - sample_height / 2)) box = (x - sample_width / 2, y - sample_height / 2, x + sample_width / 2, y + sample_height / 2) box = [int(f) for f in box] # make sure box is filled entirely poly = Image.new(mode='1', size=(box[2] - box[0], box[3] - box[1]), color=0) draw = ImageDraw.Draw(poly) vertices_shifted = [(x - box[0], y - box[1]) for (x, y) in vertices] for tri in triangles: points = [vertices_shifted[tri[t]] for t in (0, 1, 2)] draw.polygon(points, fill=1) del draw filled = True for p in poly.getdata(): if p != 1: filled = False break if filled: break if not filled: return None # create sample sample, created = ShapeImageSample.objects.get_or_create( shape=shape, defaults={'bbox': json.dumps(box)}) if created: sample_image = image.crop(box) sample.feature_vector = \ compute_wavelet_feature_vector(sample_image) save_obj_attr_image( sample, attr='image', img=sample_image, format='jpg', save=True) return sample
def save(self, *args, **kwargs): if not self.id: self.orthogonalize(save=False) if not self.image_rectified: from normals.utils import rectify_shape_from_uvnb img = rectify_shape_from_uvnb(self.shape, self) save_obj_attr_image( self, attr='image_rectified', img=img, save=False) super(ShapeRectifiedNormalLabel, self).save(*args, **kwargs)
def fill_in_bbox_task(shape): """ Helper to fill in the potentially empty image_bbox field """ image_bbox = mask_complex_polygon( image=open_image(shape.photo.image_orig), vertices=shape.vertices, triangles=shape.triangles, bbox_only=True) save_obj_attr_image(shape, attr='image_bbox', img=image_bbox, format='jpg', save=True)
def save(self, *args, **kwargs): if not self.id: self.orthogonalize(save=False) if not self.image_rectified: from normals.utils import rectify_shape_from_uvnb img = rectify_shape_from_uvnb(self.shape, self) save_obj_attr_image(self, attr='image_rectified', img=img, save=False) super(ShapeRectifiedNormalLabel, self).save(*args, **kwargs)
def fill_in_bbox_task(shape): """ Helper to fill in the potentially empty image_bbox field """ image_bbox = mask_complex_polygon(image=open_image(shape.photo.image_orig), vertices=shape.vertices, triangles=shape.triangles, bbox_only=True) save_obj_attr_image(shape, attr='image_bbox', img=image_bbox, format='jpg', save=True)
def update_shape_image_crop(shape, save=True): """ Update the cropped image for a shape """ # compute masked image image_crop, image_bbox = mask_complex_polygon( image=open_image(shape.photo.image_orig), vertices=shape.vertices, triangles=shape.triangles) if image_crop: save_obj_attr_image(shape, attr='image_crop', img=image_crop, format='png', save=save) shape.image_square_300.generate() if image_bbox: save_obj_attr_image(shape, attr='image_bbox', img=image_bbox, format='jpg', save=save) shape.image_bbox_1024.generate()
def update_shape_image_pbox(shape, padding=0.25, save=True): """ Update the pbox image for a shape """ # load image photo = shape.photo.__class__.objects.get(id=shape.photo.id) image = open_image(photo.image_orig) w, h = image.size # compute bbox vertices = [(v[0] * w, v[1] * h) for v in parse_vertices(shape.vertices)] bbox = bbox_vertices(vertices) if (len(vertices) < 3 or bbox[0] >= bbox[2] or bbox[1] >= bbox[3]): return None # compute pbox padding_x = (bbox[2] - bbox[0]) * padding padding_y = (bbox[3] - bbox[1]) * padding pbox = [ max(0, bbox[0] - padding_x), max(0, bbox[1] - padding_y), min(w, bbox[2] + padding_x), min(h, bbox[3] + padding_y), ] # make square if pbox[3] - pbox[1] > pbox[2] - pbox[0]: mid_x = 0.5 * (pbox[2] + pbox[0]) half_h = 0.5 * (pbox[3] - pbox[1]) pbox[0] = mid_x - half_h pbox[2] = mid_x + half_h if pbox[0] < 0: pbox[2] = min(w, pbox[2] - pbox[0]) pbox[0] = 0 elif pbox[2] > w: pbox[0] = max(0, pbox[0] - (pbox[2] - w)) pbox[2] = w else: mid_y = 0.5 * (pbox[3] + pbox[1]) half_w = 0.5 * (pbox[2] - pbox[0]) pbox[1] = mid_y - half_w pbox[3] = mid_y + half_w if pbox[1] < 0: pbox[3] = min(h, pbox[3] - pbox[1]) pbox[1] = 0 elif pbox[3] > h: pbox[1] = max(0, pbox[1] - (pbox[3] - h)) pbox[3] = h # crop image image_pbox = image.crop([int(v) for v in pbox]) if image_pbox: shape.pbox = json.dumps([ float(pbox[0]) / w, float(pbox[1]) / h, float(pbox[2]) / w, float(pbox[3]) / h, ]) # pbox is in units of pixels, so this gives the pixel ratio shape.pbox_aspect_ratio = float(pbox[2] - pbox[0]) / float(pbox[3] - pbox[1]) #print 'bbox: %s, pbox: %s' % (bbox, pbox) save_obj_attr_image(shape, attr='image_pbox', img=image_pbox, format='jpg', save=save) shape.image_pbox_300.generate() shape.image_pbox_512.generate()
def update_shape_image_pbox(shape, padding=0.25, save=True): """ Update the pbox image for a shape """ # load image image = open_image(shape.photo.image_orig) w, h = image.size # compute bbox vertices = [(v[0] * w, v[1] * h) for v in parse_vertices(shape.vertices)] bbox = bbox_vertices(vertices) if (len(vertices) < 3 or bbox[0] >= bbox[2] or bbox[1] >= bbox[3]): return None # compute pbox padding_x = (bbox[2] - bbox[0]) * padding padding_y = (bbox[3] - bbox[1]) * padding pbox = [ max(0, bbox[0] - padding_x), max(0, bbox[1] - padding_y), min(w, bbox[2] + padding_x), min(h, bbox[3] + padding_y), ] # make square if pbox[3] - pbox[1] > pbox[2] - pbox[0]: mid_x = 0.5 * (pbox[2] + pbox[0]) half_h = 0.5 * (pbox[3] - pbox[1]) pbox[0] = mid_x - half_h pbox[2] = mid_x + half_h if pbox[0] < 0: pbox[2] = min(w, pbox[2] - pbox[0]) pbox[0] = 0 elif pbox[2] > w: pbox[0] = max(0, pbox[0] - (pbox[2] - w)) pbox[2] = w else: mid_y = 0.5 * (pbox[3] + pbox[1]) half_w = 0.5 * (pbox[2] - pbox[0]) pbox[1] = mid_y - half_w pbox[3] = mid_y + half_w if pbox[1] < 0: pbox[3] = min(h, pbox[3] - pbox[1]) pbox[1] = 0 elif pbox[3] > h: pbox[1] = max(0, pbox[1] - (pbox[3] - h)) pbox[3] = h # crop image image_pbox = image.crop([int(v) for v in pbox]) if image_pbox: shape.pbox = json.dumps([ float(pbox[0]) / w, float(pbox[1]) / h, float(pbox[2]) / w, float(pbox[3]) / h, ]) # pbox is in units of pixels, so this gives the pixel ratio shape.pbox_aspect_ratio = float(pbox[2] - pbox[0]) / float(pbox[3] - pbox[1]) #print 'bbox: %s, pbox: %s' % (bbox, pbox) save_obj_attr_image(shape, attr='image_pbox', img=image_pbox, format='jpg', save=save) shape.image_pbox_300.generate() shape.image_pbox_512.generate()
def intrinsic_decomposition_task(photo_id, algorithm_id, task_version=0): """ Decompose an image with a given algorithm and set of parameters. The image is resized to fit in a 512 by 512 box. The resize operation happens in the file's storage colorspace (likely sRGB). :param photo_id: ``photo.id`` :param algorithm_id: ``algorithm.id`` """ #algorithm, _ = IntrinsicImagesAlgorithm.objects.get_or_create( #slug=algorithm_slug, parameters=json.dumps(parameters, sort_keys=True), #baseline=algorithm_slug.startswith('baseline_')) algorithm = IntrinsicImagesAlgorithm.objects.get(id=algorithm_id) parameters = json.loads(algorithm.parameters) if task_version != algorithm.task_version: print "Version changed (%s --> %s): exiting" % (task_version, algorithm.task_version) return elif not algorithm.active: print "Algorithm not active: %s %s: exiting" % (algorithm.slug, algorithm.parameters) return elif IntrinsicImagesDecomposition.objects.filter( photo_id=photo_id, algorithm=algorithm).exists(): print "Already decomposed: photo_id: %s, algorithm: %s %s: exiting" % ( photo_id, algorithm.slug, algorithm.parameters) return print 'intrinsic_decomposition_task: photo_id: %s, slug: %s, parameters: %s' % ( photo_id, algorithm.slug, parameters) # download image photo = Photo.objects.get(id=photo_id) image = ResizeToFit(512, 512).process(photo.open_image(width='orig')) # decompose import intrinsic.algorithm func = getattr(intrinsic.algorithm, algorithm.slug) r, s, runtime = func(image, **parameters) r = numpy_to_pil(r) s = numpy_to_pil(s) # save: use atomic so that if the image save fails, the record is not kept with transaction.atomic(): decomposition, _ = IntrinsicImagesDecomposition.objects \ .get_or_create(photo_id=photo_id, algorithm=algorithm) decomposition.runtime = runtime save_obj_attr_image(decomposition, attr='reflectance_image', img=r, format='png', save=False) save_obj_attr_image(decomposition, attr='shading_image', img=s, format='png', save=False) from intrinsic.evaluation import evaluate_error update_kwargs = evaluate_error(photo_id, r) for k, v in update_kwargs.iteritems(): setattr(decomposition, k, v) decomposition.save()
def _run_algorithm(photo, function, slug, parameters={}, image_size=512, baseline=False): """ Sets up an algorithm in the database, calls ``function``, then stores the result in the database """ if not isinstance(photo, Photo): time_start = timeit.default_timer() r, s = function(image=photo, **parameters) time_end = timeit.default_timer() runtime = time_end - time_start r, s = [process_layer(x) for x in (r, s)] return r, s, runtime # ensure a consistent order for parameters algorithm, _ = IntrinsicImagesAlgorithm.objects.get_or_create( slug=slug, parameters=json.dumps(parameters, sort_keys=True), baseline=baseline) if IntrinsicImagesDecomposition.objects.filter( photo=photo, algorithm=algorithm).exists(): print '_run_algorithm: EXISTS: photo %s, algorithm %s, params %s' % ( photo.id, slug, parameters) return else: print '_run_algorithm: starting: photo %s, algorithm %s, params %s' % ( photo.id, slug, parameters) # load and resize image (do it here rather than load the pre-resized photo # thumbnail to avoid jpg artifacts) attr = '_intrinsic_algorithm_photo_%s' % image_size if hasattr(photo, attr): image = getattr(photo, attr) else: image = photo.open_image(width='orig') image = ResizeToFit(image_size, image_size).process(image) setattr(photo, attr, image) time_start = timeit.default_timer() # r, s: linar numpy arrays r, s = function(image=image, **parameters) time_end = timeit.default_timer() runtime = time_end - time_start # r, s: sRGB numpy arrays r, s = [process_layer(x) for x in (r, s)] # r, s: sRGB PIL images reflectance_image = numpy_to_pil(r) shading_image = numpy_to_pil(s) # save in database with transaction.atomic(): decomposition, _ = IntrinsicImagesDecomposition.objects \ .get_or_create(photo=photo, algorithm=algorithm) # fill in fields decomposition.runtime = runtime save_obj_attr_image( decomposition, attr='reflectance_image', img=reflectance_image, format='png', save=False) save_obj_attr_image( decomposition, attr='shading_image', img=shading_image, format='png', save=False) # comupte error from intrinsic.evaluation import evaluate_error update_kwargs = evaluate_error(photo.id, reflectance_image) for k, v in update_kwargs.iteritems(): setattr(decomposition, k, v) # save to database decomposition.save() print '_run_algorithm: DONE: photo %s, algorithm %s, params %s, runtime: %s' % ( photo.id, slug, parameters, runtime) return r, s, runtime
def _run_algorithm(photo, function, slug, parameters={}, image_size=512, baseline=False): """ Sets up an algorithm in the database, calls ``function``, then stores the result in the database """ if not isinstance(photo, Photo): time_start = timeit.default_timer() r, s = function(image=photo, **parameters) time_end = timeit.default_timer() runtime = time_end - time_start r, s = [process_layer(x) for x in (r, s)] return r, s, runtime # ensure a consistent order for parameters algorithm, _ = IntrinsicImagesAlgorithm.objects.get_or_create( slug=slug, parameters=json.dumps(parameters, sort_keys=True), baseline=baseline) if IntrinsicImagesDecomposition.objects.filter( photo=photo, algorithm=algorithm).exists(): print '_run_algorithm: EXISTS: photo %s, algorithm %s, params %s' % ( photo.id, slug, parameters) return else: print '_run_algorithm: starting: photo %s, algorithm %s, params %s' % ( photo.id, slug, parameters) # load and resize image (do it here rather than load the pre-resized photo # thumbnail to avoid jpg artifacts) attr = '_intrinsic_algorithm_photo_%s' % image_size if hasattr(photo, attr): image = getattr(photo, attr) else: image = photo.open_image(width='orig') image = ResizeToFit(image_size, image_size).process(image) setattr(photo, attr, image) time_start = timeit.default_timer() # r, s: linar numpy arrays r, s = function(image=image, **parameters) time_end = timeit.default_timer() runtime = time_end - time_start # r, s: sRGB numpy arrays r, s = [process_layer(x) for x in (r, s)] # r, s: sRGB PIL images reflectance_image = numpy_to_pil(r) shading_image = numpy_to_pil(s) # save in database with transaction.atomic(): decomposition, _ = IntrinsicImagesDecomposition.objects \ .get_or_create(photo=photo, algorithm=algorithm) # fill in fields decomposition.runtime = runtime save_obj_attr_image(decomposition, attr='reflectance_image', img=reflectance_image, format='png', save=False) save_obj_attr_image(decomposition, attr='shading_image', img=shading_image, format='png', save=False) # comupte error from intrinsic.evaluation import evaluate_error update_kwargs = evaluate_error(photo.id, reflectance_image) for k, v in update_kwargs.iteritems(): setattr(decomposition, k, v) # save to database decomposition.save() print '_run_algorithm: DONE: photo %s, algorithm %s, params %s, runtime: %s' % ( photo.id, slug, parameters, runtime) return r, s, runtime