def rotateBitmap(fname, angle): """Rotate bitmap, (almost) lossless for jpg. args: filename (str): name of a file angle (int): 0, 90, 180, 270, or -90 degree rotation. If available, this routine uses the `jpegtran-cffi` library for lossless jpeg rotations. If that library is not available, there will be a loss of quality when rotating a jpeg image. TODO: think about multiples of 8/16 thing for jpeg. """ assert angle in (0, 90, 180, 270, -90), "Invalid rotation angle {}".format(angle) if angle == 0: return fnamebase, fnameext = os.path.splitext(fname) if fnameext.lower() in (".jpg", ".jpeg"): if have_jpegtran: print("**** Doing JPEG rotation {} on {}".format(angle, fname)) im = jpegtran.JPEGImage(fname) im.rotate(angle).save(fname) return warn( f"Doing LOSSY jpeg rotation {angle} on {fname} [b/c jpegtran-cffi not installed]" ) subprocess.run( ["mogrify", "-quiet", "-rotate", str(angle), fname], stderr=subprocess.STDOUT, shell=False, check=True, )
def normalizeJPEGOrientation(f): """Transform image according to its Exif metadata. Gives a warning if size not a multiple 16 b/c underlying library just quietly mucks up the bottom/right edge: https://github.com/jbaiter/jpegtran-cffi/issues/23 In Plom, we generally transcode jpeg's that are not multiples of 16. """ im = jpegtran.JPEGImage(f) if im.exif_orientation: if im.width % 16 or im.height % 16: warnings.warn( ' JPEG image "{}" dims not mult of 16: re-orientations may be lossy' .format(f)) im2 = im.exif_autotransform() print(' normalizing "{}" {}x{} to "{}" {}x{}'.format( im.exif_orientation, im.width, im.height, im2.exif_orientation, im2.width, im2.height, )) # str to workaround https://github.com/jbaiter/jpegtran-cffi/issues/28 im2.save(str(f))
def normalizeJPEGOrientation(f): """Transform image according to its Exif metadata. args: f (pathlib.Path/str): a jpeg file. Gives a warning if size not a multiple 16 b/c underlying library just quietly mucks up the bottom/right edge: https://github.com/jbaiter/jpegtran-cffi/issues/23 In Plom, we generally transcode jpeg's that are not multiples of 16. """ # First use Pillow: return early if we can, in case we don't have jpegtran pil_img = PIL.Image.open(f) exif = pil_img.getexif() all_tags = PIL.ExifTags.TAGS orientations = [ v for k, v in exif.items() if all_tags.get(k) == "Orientation" ] if not orientations or orientations[0] == 0: return if orientations[0] == 1: return if not have_jpegtran: warn( f" jpeg {f} has EXIF orientation but no jpegtran-cffi: skipping orientation normalize" ) return im = jpegtran.JPEGImage(f) if not im.exif_orientation: return if im.width % 16 or im.height % 16: warn(f' jpeg "{f}" dims not mult of 16: re-orientations may be lossy') im2 = im.exif_autotransform() print(' normalizing "{}" {}x{} to "{}" {}x{}'.format( im.exif_orientation, im.width, im.height, im2.exif_orientation, im2.width, im2.height, )) # str to workaround https://github.com/jbaiter/jpegtran-cffi/issues/28 im2.save(str(f))
def rotateBitmap(fname, angle): """Rotate bitmap, (almost) lossless for jpg. args: filename (str): name of a file angle (int): 90, 180, or 270 degree rotation TODO: think about multiples of 8/16 thing for jpeg. """ fnamebase, fnameext = os.path.splitext(fname) if fnameext.lower() in (".jpg", ".jpeg"): print("**** Doing JPEG rotation {} on {}".format(angle, fname)) im = jpegtran.JPEGImage(fname) im.rotate(angle).save(fname) return subprocess.run( ["mogrify", "-quiet", "-rotate", str(angle), fname], stderr=subprocess.STDOUT, shell=False, check=True, )
def test_get_page_image_thumb(client): # TODO: Use test images that actually have an EXIF thumbnail... wfid = create_workflow(client) rv = client.get('/api/workflow/{0}/page/1/raw/thumb'.format(wfid)) assert rv.status_code == 200 assert jpegtran.JPEGImage(blob=rv.data).width == 196
def test_get_page_image_scaled(client): wfid = create_workflow(client) rv = client.get('/api/workflow/{0}/page/0/raw?width=300'.format(wfid)) assert rv.status_code == 200 img = jpegtran.JPEGImage(blob=rv.data) assert img.width == 300
def test_get_workflow_image_scaled(client): wfid = create_workflow(client) img = jpegtran.JPEGImage(blob=client.get( '/api/workflow/{0}/image/0?width=300'.format(wfid)).data) assert img.width == 300