{"id":12022,"date":"2014-08-06T13:52:44","date_gmt":"2014-08-06T11:52:44","guid":{"rendered":"https:\/\/blog.trifork.com\/?p=12022"},"modified":"2014-08-06T13:52:44","modified_gmt":"2014-08-06T11:52:44","slug":"scaling-images-quirks-and-tricks","status":"publish","type":"post","link":"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/","title":{"rendered":"Scaling images &#8211; Quirks and tricks"},"content":{"rendered":"<p style=\"text-align: justify\">In one of our projects we created functionality to upload images. These images can, after being uploaded, be viewed on a grid or on\u00a0 a detail page. The grid shows a thumbnail and the detail page shows a medium variant of the image. To create these variants we have used java ImageIO, imgscalr, ImageMagick (im4java) and Exiftool. Sounds like quite a few libraries to create two different sized images, but it\u2019s the result of trying to support as many images as possible and provide a good user experience. In this blog post I will explain how and why we have used these libraries to solve the problems we encountered.<\/p>\n<p><figure id=\"attachment_12023\" aria-describedby=\"caption-attachment-12023\" style=\"width: 570px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2014\/06\/upload_flow.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-12023\" src=\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2014\/06\/upload_flow-289x300.jpg\" alt=\"\" width=\"570\" height=\"436\" \/><\/a><figcaption id=\"caption-attachment-12023\" class=\"wp-caption-text\"><em>Process flow (Click for larger view)<\/em><\/figcaption><\/figure><\/p>\n<p><!--more--><\/p>\n<p style=\"text-align: justify\">In our application we allow the user to upload the following file types: *.jp(e)g, *.png, *.tif(f).\u00a0 For .png and .tiff files we have to do some extra steps before we can scale the image to a thumbnail and medium size variant. I will elaborate on that part later on. As you can see in the flow diagram, we store the full size image in the database. After that an event is started to create an asynchronous process. First lets take a look at how we handle .jp(e)g files.\u00a0 We create a BufferedImage from our source file, which is a Path in our case:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\nBufferedImage bufferedImage =\u00a0 ImageIO.read(source.toFile());\n<\/pre>\n<p style=\"text-align: justify\">Well that was really easy, but with this one-liner we already discovered some problems during the process of testing some images. Not all images (no matter what content type, even if there is a reader for that content type) could be read by ImageIO.\u00a0 For example, one of the images contained a color code that is not supported. Instead of trying to support all kind of scenarios and the risk of missing one, we decided added a fallback that we convert the image to jpg file with ImageMagick. Then the with converted image we try to read the image again with ImageIO.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\nprivate BufferedImage createBufferedImage(Path source)\nthrows IOException {\n  try {\n    \/\/ First try to create bufferedImage with original source\n    return ImageIO.read(source.toFile());\n  } catch (IOException e) {\n    \/\/ If failed to create bufferedImage with original source,\n    \/\/ try to create bufferedImage with converted jpg.\n    \/\/ If that also fails, we can not handle the image and\n    \/\/ the exception needs to be handled.\n    Path imageWithoutExifMetadata =\n            copyImageWithoutExifMetadataFromImage(source);\n    Path convertedSource = convertToJpg(imageWithoutExifMetadata);\n    BufferedImage bufferedImage =\n            ImageIO.read(convertedSource.toFile());\n\n    cleanupTemporaryConvertFiles(\n            convertedSource, imageWithoutExifMetadata);\n\n    return bufferedImage;\n  }\n}\n<\/pre>\n<p>Now that we have a BufferedImage we can use this to scale the image with imgscalr.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\nBufferedImage thumbnailed = Scalr.resize(bufferedImage,\n        jpgImageQuality, Scalr.Mode.AUTOMATIC, newImageSize);\n<\/pre>\n<p>When the image is scaled we need to write the image:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\nByteArrayOutputStream baos = new ByteArrayOutputStream();\nImageOutputStream ios = ImageIO.createImageOutputStream(baos);\nIterator&lt;ImageWriter&gt; iter = ImageIO.getImageWritersByFormatName(&quot;jpeg&quot;);\nImageWriter writer = iter.next();\nImageWriteParam iwp = writer.getDefaultWriteParam();\niwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);\niwp.setCompressionQuality(jpgImageCompression);\nif (iwp.canWriteProgressive() &amp;&amp; originalFileSize &gt; TEN_KB) {\n  iwp.setProgressiveMode(ImageWriteParam.MODE_DEFAULT);\n}\nwriter.setOutput(ios);\nwriter.write(null, new IIOImage(thumbnailed, null, null), iwp);\nwriter.dispose();\n\nbaos.flush();\nbyte&#x5B;] imageInByte = baos.toByteArray();\nbaos.close();\n<\/pre>\n<p style=\"text-align: justify\">As I said before we convert the image with ImageMagick in case we can not read it properly. Also here we added a few optimisations to convert the most images. First we have added the quiet mode option to ignore some of the warnings that made the conversion fail.<\/p>\n<p style=\"text-align: justify\">Sometimes images have multiple layers and ImageMagick will convert an image for each of those layers. Because we only need one image that we can use in our process, we always ask for the first layer. This can be done by adding [0] to the filename.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\nConvertCmd cmd = new ConvertCmd();\ncmd.setSearchPath(pathToImageMagick);\n\nIMOperation op = new IMOperation();\n\n\/\/ We addded the quiet mode to ignore some warnings which made the conversion failed\nop.quiet();\nop.colorspace(&quot;sRGB&quot;);\n\/\/Take the first layer (&#x5B;0]) of the image to convert\nop.addImage(imageFile.toAbsolutePath().toString() + &quot;&#x5B;0]&quot;);\nop.addImage(imageFile.toAbsolutePath().toString() + JPG_EXTENSION);\n\ncmd.run(op);\n<\/pre>\n<h3>Tiff files<\/h3>\n<p style=\"text-align: justify\">Tiff files can not be read by ImageIO, because there is no reader available. So each tiff file is converted to a jpg file using ImageMagick. This works fine, except for a few images we tried. These images failed to convert because of a bug in the underlying library libtif. After some heavy research I found that the conversion failed because the metadata could not be read properly.\u00a0 The best way to solve this problem was to upgrade the libtif library (as suggested on the <a href=\"http:\/\/www.imagemagick.org\/discourse-server\/viewtopic.php?f=1&amp;t=25242\">ImageMagick forum<\/a>), but that was a bit of a problem on our operating system. Therefore we decided to remove the metadata with Exiftool from the tiff file and convert the image without the metadata. The converted image is then used for creating the thumbnail and medium variant.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\nprivate Path copyImageWithoutExifMetadataFromImage(Path imageFile) {\n  Path tempImage;\n\n  try {\n    tempImage = Files.createFile(\n            buildImageWithoutMetadataFilePath(imageFile));\n    Files.copy(imageFile, tempImage,\n            StandardCopyOption.REPLACE_EXISTING);\n\n    metadataWriter.deleteExifMetadata(tempImage);\n  } catch (IOException e) {\n    logger.error(&quot;Failed to delete exif metadata from image &quot; +\n            imageFile.toAbsolutePath().toString(), e);\n    throw new ImageScalingException(e);\n  }\n  return tempImage;\n}\n<\/pre>\n<p style=\"text-align: justify\">We now have a process in place that creates image variants for probably all our images. This gives us a solid base to upload the images and don&#8217;t bother the users with upload exceptions. Hopefully this process helps some of you in scaling, converting or creating images.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In one of our projects we created functionality to upload images. These images can, after being uploaded, be viewed on a grid or on\u00a0 a detail page. The grid shows a thumbnail and the detail page shows a medium variant of the image. To create these variants we have used java ImageIO, imgscalr, ImageMagick (im4java) [&hellip;]<\/p>\n","protected":false},"author":102,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"content-type":"","footnotes":""},"categories":[10],"tags":[360,384,385,386],"class_list":["post-12022","post","type-post","status-publish","format-standard","hentry","category-development","tag-exiftool","tag-imageio","tag-imagemagick","tag-imgscalr"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Scaling images - Quirks and tricks - Trifork Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Scaling images - Quirks and tricks - Trifork Blog\" \/>\n<meta property=\"og:description\" content=\"In one of our projects we created functionality to upload images. These images can, after being uploaded, be viewed on a grid or on\u00a0 a detail page. The grid shows a thumbnail and the detail page shows a medium variant of the image. To create these variants we have used java ImageIO, imgscalr, ImageMagick (im4java) [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/\" \/>\n<meta property=\"og:site_name\" content=\"Trifork Blog\" \/>\n<meta property=\"article:published_time\" content=\"2014-08-06T11:52:44+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2014\/06\/upload_flow-289x300.jpg\" \/>\n<meta name=\"author\" content=\"Roberto van der Linden\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Roberto van der Linden\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/\",\"url\":\"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/\",\"name\":\"Scaling images - Quirks and tricks - Trifork Blog\",\"isPartOf\":{\"@id\":\"https:\/\/trifork.nl\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2014\/06\/upload_flow-289x300.jpg\",\"datePublished\":\"2014-08-06T11:52:44+00:00\",\"author\":{\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/037974cf3e24a7b09a93770b190d6e35\"},\"breadcrumb\":{\"@id\":\"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/#primaryimage\",\"url\":\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2014\/06\/upload_flow-289x300.jpg\",\"contentUrl\":\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2014\/06\/upload_flow-289x300.jpg\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/trifork.nl\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Scaling images &#8211; Quirks and tricks\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/trifork.nl\/blog\/#website\",\"url\":\"https:\/\/trifork.nl\/blog\/\",\"name\":\"Trifork Blog\",\"description\":\"Keep updated on the technical solutions Trifork is working on!\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/trifork.nl\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/037974cf3e24a7b09a93770b190d6e35\",\"name\":\"Roberto van der Linden\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/afe49faf7ef8dd3753baefb334568b10?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/afe49faf7ef8dd3753baefb334568b10?s=96&d=mm&r=g\",\"caption\":\"Roberto van der Linden\"},\"url\":\"https:\/\/trifork.nl\/blog\/author\/roberto\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Scaling images - Quirks and tricks - Trifork Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/","og_locale":"en_US","og_type":"article","og_title":"Scaling images - Quirks and tricks - Trifork Blog","og_description":"In one of our projects we created functionality to upload images. These images can, after being uploaded, be viewed on a grid or on\u00a0 a detail page. The grid shows a thumbnail and the detail page shows a medium variant of the image. To create these variants we have used java ImageIO, imgscalr, ImageMagick (im4java) [&hellip;]","og_url":"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/","og_site_name":"Trifork Blog","article_published_time":"2014-08-06T11:52:44+00:00","og_image":[{"url":"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2014\/06\/upload_flow-289x300.jpg","type":"","width":"","height":""}],"author":"Roberto van der Linden","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Roberto van der Linden","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/","url":"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/","name":"Scaling images - Quirks and tricks - Trifork Blog","isPartOf":{"@id":"https:\/\/trifork.nl\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/#primaryimage"},"image":{"@id":"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/#primaryimage"},"thumbnailUrl":"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2014\/06\/upload_flow-289x300.jpg","datePublished":"2014-08-06T11:52:44+00:00","author":{"@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/037974cf3e24a7b09a93770b190d6e35"},"breadcrumb":{"@id":"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/#primaryimage","url":"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2014\/06\/upload_flow-289x300.jpg","contentUrl":"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2014\/06\/upload_flow-289x300.jpg"},{"@type":"BreadcrumbList","@id":"https:\/\/trifork.nl\/blog\/scaling-images-quirks-and-tricks\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/trifork.nl\/blog\/"},{"@type":"ListItem","position":2,"name":"Scaling images &#8211; Quirks and tricks"}]},{"@type":"WebSite","@id":"https:\/\/trifork.nl\/blog\/#website","url":"https:\/\/trifork.nl\/blog\/","name":"Trifork Blog","description":"Keep updated on the technical solutions Trifork is working on!","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/trifork.nl\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/037974cf3e24a7b09a93770b190d6e35","name":"Roberto van der Linden","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/afe49faf7ef8dd3753baefb334568b10?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/afe49faf7ef8dd3753baefb334568b10?s=96&d=mm&r=g","caption":"Roberto van der Linden"},"url":"https:\/\/trifork.nl\/blog\/author\/roberto\/"}]}},"_links":{"self":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts\/12022","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/users\/102"}],"replies":[{"embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/comments?post=12022"}],"version-history":[{"count":0,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts\/12022\/revisions"}],"wp:attachment":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/media?parent=12022"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/categories?post=12022"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/tags?post=12022"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}