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