├── LICENSE ├── README.md ├── code ├── images │ ├── bmp │ │ └── marble.bmp │ ├── bombs │ │ ├── picture-100M-6000x6000.gif │ │ ├── picture-100M-6000x6000.png │ │ └── picture-1G-19000x19000.png │ ├── gif │ │ ├── house-photo.gif │ │ ├── marble.gif │ │ └── test-image-transparent.gif │ ├── jbig │ │ └── test-image-01.jb2 │ ├── jp2 │ │ └── marble.jp2 │ ├── jpg │ │ ├── marble.jpg │ │ ├── test-image-cmyk-lzw.jpg │ │ ├── test-image-cmyk-uncompressed.jpg │ │ ├── test-image-differental-sequential-arithmentic.jpg │ │ ├── test-image-differential-progressive-arithmetic.jpg │ │ └── test-image-rgb-01.jpg │ ├── png │ │ ├── marble.png │ │ ├── test-image-48-bit-reduced-to-24-bit.png │ │ └── test-image-transparent.png │ ├── samples │ │ ├── corrupted-image.png │ │ ├── diff.png │ │ ├── house-photo.gif │ │ ├── open-office-01.pdf.300.0.png │ │ ├── open-office-02.pdf.300.0.png │ │ ├── under-exposed-black-white-image.jpg │ │ └── under-exposed-color-image.jpg │ ├── scans │ │ ├── scan-jpg-color-300-dpi.pdf │ │ └── scan-jpg-color-600-dpi.pdf │ ├── tiff │ │ ├── marble.tiff │ │ ├── test-multi-gray-compression-type-4.tiff │ │ ├── test-multi-rgb-compression-type-7.tiff │ │ ├── test-single-cmyk-compression-lzw.tiff │ │ ├── test-single-gray-compression-lzw.tiff │ │ ├── test-single-gray-compression-type-2.tiff │ │ ├── test-single-gray-compression-type-3.tiff │ │ ├── test-single-gray-compression-type-4.tiff │ │ └── test-single-rgb-uncompressed.tiff │ └── willhaben │ │ ├── willhaben-01.jpg │ │ ├── willhaben-02.jpg │ │ ├── willhaben-03.jpg │ │ ├── willhaben-04.jpg │ │ ├── willhaben-05.jpg │ │ ├── willhaben-06.jpg │ │ ├── willhaben-07.jpg │ │ └── willhaben-08.jpg ├── jipsg │ ├── .gitignore │ ├── common │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ └── java │ │ │ └── org │ │ │ └── github │ │ │ └── jipsg │ │ │ └── common │ │ │ ├── AbstractImageTest.java │ │ │ └── image │ │ │ ├── BufferedImageFactory.java │ │ │ ├── BufferedImageOperations.java │ │ │ ├── BufferedImageUtils.java │ │ │ └── ops │ │ │ ├── AlphaChannelImageOp.java │ │ │ └── InvertImageOp.java │ ├── image-manipulation │ │ ├── pom.xml │ │ └── src │ │ │ └── test │ │ │ └── java │ │ │ └── org │ │ │ └── github │ │ │ └── jipsg │ │ │ └── image │ │ │ ├── comparison │ │ │ ├── IdenticalImageComparator.java │ │ │ ├── IdenticalImageComparatorTest.java │ │ │ ├── ImageComparator.java │ │ │ ├── InvertedImageComparatorTest.java │ │ │ └── InvertingImageComparator.java │ │ │ ├── filter │ │ │ └── AutoCorrectionFilter.java │ │ │ └── manipulation │ │ │ └── BaseImageManipulationTest.java │ ├── imageio │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ └── org │ │ │ │ └── github │ │ │ │ └── jipsg │ │ │ │ └── imageio │ │ │ │ └── BaseImageIoTest.java │ │ │ └── test │ │ │ └── java │ │ │ └── org │ │ │ └── github │ │ │ └── jipsg │ │ │ └── imageio │ │ │ ├── ImageConversionImageIoTest.java │ │ │ ├── ImageLoadImageIoTest.java │ │ │ └── ImageResamplingImageIoTest.java │ ├── jai │ │ ├── pom.xml │ │ └── src │ │ │ └── test │ │ │ └── java │ │ │ └── org │ │ │ └── github │ │ │ └── jipsg │ │ │ └── jai │ │ │ ├── AbstractJaiTest.java │ │ │ ├── ImageConversionJaiTest.java │ │ │ ├── ImageLoadJaiTest.java │ │ │ └── ImageResamplingJaiTest.java │ ├── pdfbox │ │ ├── pom.xml │ │ └── src │ │ │ └── test │ │ │ └── java │ │ │ └── org │ │ │ └── github │ │ │ └── jipsg │ │ │ └── pdfbox │ │ │ └── PdfBoxPreviewTest.java │ ├── pom.xml │ ├── readme.md │ ├── sanselan │ │ ├── pom.xml │ │ └── src │ │ │ └── test │ │ │ └── java │ │ │ └── org │ │ │ └── github │ │ │ └── jipsg │ │ │ └── sanselan │ │ │ ├── BaseSanselanTest.java │ │ │ ├── ImageConversionSanselanTest.java │ │ │ ├── ImageResamplingSanselanTest.java │ │ │ ├── LoadImageSanselanTest.java │ │ │ └── ManagedImageBufferedImageFactory.java │ ├── thumbnailator │ │ ├── pom.xml │ │ └── src │ │ │ └── test │ │ │ └── java │ │ │ └── org │ │ │ └── github │ │ │ └── jipsg │ │ │ └── thumbnailator │ │ │ └── ImageScalingThumbnailatorTest.java │ └── twelvemonkeys │ │ ├── pom.xml │ │ └── src │ │ └── test │ │ └── java │ │ └── org │ │ └── github │ │ └── jipsg │ │ └── twelvemonkeys │ │ ├── BaseTwelveMonkeysTest.java │ │ ├── ImageConversionTwelveMonkeysTest.java │ │ ├── ImageLoadTwelveMonkeysTest.java │ │ ├── ImageResamplingTwelveMonkeysTest.java │ │ └── ImageScalingTwelveMonkeysTest.java ├── libs │ ├── commons-imaging-1.0-20160915.001141-89.jar │ ├── jai_codec-1.1.2_01.jar │ ├── jai_core-1.1.3.jar │ ├── jai_imageio-1.1.jar │ ├── mvn-install-file.bat │ ├── mvn-install-file.sh │ └── readme.md └── pdf │ ├── erste-document-01.pdf │ ├── sigice9_172.CVISION.pdf │ └── test-large-scan.pdf ├── paper ├── README.md └── images │ ├── alpha-channel-after.png │ ├── alpha-channel-before.png │ ├── house-after.jpg │ ├── house-before.gif │ ├── pdf-jbig2-image-result.jpg │ ├── pdf-jbig2-image-source.jpg │ ├── willhaben-advert-desktop-small.png │ ├── willhaben-advert-desktop.png │ ├── willhaben-advert-mobile-small.png │ └── willhaben-advert-mobile.png └── slides ├── images ├── 12-monkeys-505001e4d6467.png ├── TwelveMonkeys ImageIO_logo.pdf ├── challanger-launch-wikipedia.jpg ├── large-image-scan-pdf.jpg ├── logo │ ├── bouvet.png │ └── willhaben.png ├── pdf-jbig2-image-result.jpg ├── pdf-jbig2-image-source.jpg ├── willhaben-advert-desktop.png └── willhaben-advert-mobile.png ├── jipsg.key ├── jipsg.pdf ├── twelvemonkeys-slides.txt └── twelvemonkeys-slides2.txt /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Java Image Processing Survival Guide 2 | 3 | For a few customers I needed to do a lot of things with images 4 | 5 | * Process million of images - many of them were user-supplied 6 | * Convert various image formats (JPG, GIF, PNG, TIFF, BMP) into JPEGs 7 | * Converting PDFs into JPEGs 8 | * Resample images 9 | * Handle CMYK image 10 | * Handle images with alpha-channel 11 | * Apply image operations such as geyscaling, dithering and sharpening 12 | * Setting image compression and DPI for storing JPGs 13 | 14 | In order to get the things done I used various image processing libraries with varying success 15 | 16 | * Java ImageIO (comes with the JDK) 17 | * Java Advanced Imaging (JAI) 18 | * Apache Commons Imaging (also known as Sanselan) 19 | * TwelveMonkeys library 20 | 21 | The problem with all of those libraries is that I had various issues and information how to solve my issues were hard to find on the internet. 22 | 23 | Therefore the idea was born to write a paper & sample code covering the image processing libraries I used - maybe you can save a lot of time :-) 24 | -------------------------------------------------------------------------------- /code/images/bmp/marble.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/bmp/marble.bmp -------------------------------------------------------------------------------- /code/images/bombs/picture-100M-6000x6000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/bombs/picture-100M-6000x6000.gif -------------------------------------------------------------------------------- /code/images/bombs/picture-100M-6000x6000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/bombs/picture-100M-6000x6000.png -------------------------------------------------------------------------------- /code/images/bombs/picture-1G-19000x19000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/bombs/picture-1G-19000x19000.png -------------------------------------------------------------------------------- /code/images/gif/house-photo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/gif/house-photo.gif -------------------------------------------------------------------------------- /code/images/gif/marble.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/gif/marble.gif -------------------------------------------------------------------------------- /code/images/gif/test-image-transparent.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/gif/test-image-transparent.gif -------------------------------------------------------------------------------- /code/images/jbig/test-image-01.jb2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/jbig/test-image-01.jb2 -------------------------------------------------------------------------------- /code/images/jp2/marble.jp2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/jp2/marble.jp2 -------------------------------------------------------------------------------- /code/images/jpg/marble.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/jpg/marble.jpg -------------------------------------------------------------------------------- /code/images/jpg/test-image-cmyk-lzw.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/jpg/test-image-cmyk-lzw.jpg -------------------------------------------------------------------------------- /code/images/jpg/test-image-cmyk-uncompressed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/jpg/test-image-cmyk-uncompressed.jpg -------------------------------------------------------------------------------- /code/images/jpg/test-image-differental-sequential-arithmentic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/jpg/test-image-differental-sequential-arithmentic.jpg -------------------------------------------------------------------------------- /code/images/jpg/test-image-differential-progressive-arithmetic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/jpg/test-image-differential-progressive-arithmetic.jpg -------------------------------------------------------------------------------- /code/images/jpg/test-image-rgb-01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/jpg/test-image-rgb-01.jpg -------------------------------------------------------------------------------- /code/images/png/marble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/png/marble.png -------------------------------------------------------------------------------- /code/images/png/test-image-48-bit-reduced-to-24-bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/png/test-image-48-bit-reduced-to-24-bit.png -------------------------------------------------------------------------------- /code/images/png/test-image-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/png/test-image-transparent.png -------------------------------------------------------------------------------- /code/images/samples/corrupted-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/samples/corrupted-image.png -------------------------------------------------------------------------------- /code/images/samples/diff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/samples/diff.png -------------------------------------------------------------------------------- /code/images/samples/house-photo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/samples/house-photo.gif -------------------------------------------------------------------------------- /code/images/samples/open-office-01.pdf.300.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/samples/open-office-01.pdf.300.0.png -------------------------------------------------------------------------------- /code/images/samples/open-office-02.pdf.300.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/samples/open-office-02.pdf.300.0.png -------------------------------------------------------------------------------- /code/images/samples/under-exposed-black-white-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/samples/under-exposed-black-white-image.jpg -------------------------------------------------------------------------------- /code/images/samples/under-exposed-color-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/samples/under-exposed-color-image.jpg -------------------------------------------------------------------------------- /code/images/scans/scan-jpg-color-300-dpi.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/scans/scan-jpg-color-300-dpi.pdf -------------------------------------------------------------------------------- /code/images/scans/scan-jpg-color-600-dpi.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/scans/scan-jpg-color-600-dpi.pdf -------------------------------------------------------------------------------- /code/images/tiff/marble.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/tiff/marble.tiff -------------------------------------------------------------------------------- /code/images/tiff/test-multi-gray-compression-type-4.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/tiff/test-multi-gray-compression-type-4.tiff -------------------------------------------------------------------------------- /code/images/tiff/test-multi-rgb-compression-type-7.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/tiff/test-multi-rgb-compression-type-7.tiff -------------------------------------------------------------------------------- /code/images/tiff/test-single-cmyk-compression-lzw.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/tiff/test-single-cmyk-compression-lzw.tiff -------------------------------------------------------------------------------- /code/images/tiff/test-single-gray-compression-lzw.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/tiff/test-single-gray-compression-lzw.tiff -------------------------------------------------------------------------------- /code/images/tiff/test-single-gray-compression-type-2.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/tiff/test-single-gray-compression-type-2.tiff -------------------------------------------------------------------------------- /code/images/tiff/test-single-gray-compression-type-3.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/tiff/test-single-gray-compression-type-3.tiff -------------------------------------------------------------------------------- /code/images/tiff/test-single-gray-compression-type-4.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/tiff/test-single-gray-compression-type-4.tiff -------------------------------------------------------------------------------- /code/images/tiff/test-single-rgb-uncompressed.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/tiff/test-single-rgb-uncompressed.tiff -------------------------------------------------------------------------------- /code/images/willhaben/willhaben-01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/willhaben/willhaben-01.jpg -------------------------------------------------------------------------------- /code/images/willhaben/willhaben-02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/willhaben/willhaben-02.jpg -------------------------------------------------------------------------------- /code/images/willhaben/willhaben-03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/willhaben/willhaben-03.jpg -------------------------------------------------------------------------------- /code/images/willhaben/willhaben-04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/willhaben/willhaben-04.jpg -------------------------------------------------------------------------------- /code/images/willhaben/willhaben-05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/willhaben/willhaben-05.jpg -------------------------------------------------------------------------------- /code/images/willhaben/willhaben-06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/willhaben/willhaben-06.jpg -------------------------------------------------------------------------------- /code/images/willhaben/willhaben-07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/willhaben/willhaben-07.jpg -------------------------------------------------------------------------------- /code/images/willhaben/willhaben-08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/images/willhaben/willhaben-08.jpg -------------------------------------------------------------------------------- /code/jipsg/.gitignore: -------------------------------------------------------------------------------- 1 | # git-ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | .DS_Store 8 | *.iml 9 | *.iws 10 | *.ipr 11 | target 12 | .swp 13 | .idea 14 | .classpath 15 | .project 16 | .settings -------------------------------------------------------------------------------- /code/jipsg/common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | org.github.jipsg 7 | jipsg-common 8 | 0.0.1-SNAPSHOT 9 | jipsg-common 10 | 2014 11 | Java Image Processing Survival Guide ImageIO 12 | 13 | 14 | 15 | junit 16 | junit 17 | 4.11 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /code/jipsg/common/src/main/java/org/github/jipsg/common/AbstractImageTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.common; 18 | 19 | import java.awt.image.BufferedImage; 20 | import java.io.File; 21 | import java.io.IOException; 22 | 23 | import static org.junit.Assert.assertNotNull; 24 | import static org.junit.Assert.assertTrue; 25 | 26 | /** 27 | * Abstract test class for image testing. 28 | */ 29 | public abstract class AbstractImageTest { 30 | 31 | private String moduleName; 32 | private File imageDirectory; 33 | 34 | public abstract BufferedImage createBufferedImage(final File file) throws Exception; 35 | 36 | public abstract void writeBufferedImage(final BufferedImage bufferedImage, final String formatName, final File file) throws Exception; 37 | 38 | public abstract BufferedImage resample(final BufferedImage bufferedImage, int width, int height); 39 | 40 | public abstract void writeBufferedImage(BufferedImage bufferedImage, float quality, int dpi, String formatName, File file) throws Exception; 41 | 42 | public void setup() { 43 | File currDirectory = new File(""); 44 | if (currDirectory.getAbsolutePath().endsWith("jipsg")) { 45 | this.imageDirectory = new File("../images"); 46 | } else { 47 | this.imageDirectory = new File("../../images"); 48 | } 49 | } 50 | 51 | protected void setModuleName(String moduleName) { 52 | this.moduleName = moduleName; 53 | } 54 | 55 | /** 56 | * Get the given file. 57 | * 58 | * @param folderName Name of the folder under "images" 59 | * @param fileName File name 60 | * @return the file 61 | */ 62 | protected File getImageFile(String folderName, String fileName) throws IOException { 63 | File folderFile = new File(getImageDirectory(), folderName); 64 | File result = new File(folderFile, fileName); 65 | if (!result.exists() || !result.canRead()) { 66 | throw new IOException("Can't open/read the following file : " + result.getAbsolutePath()); 67 | } 68 | return result; 69 | } 70 | 71 | /** 72 | * Some dumb sanity check that we have a valid buffered image. 73 | */ 74 | protected void assertValidBufferedImage(BufferedImage bufferedImage) { 75 | assertNotNull("bufferedImage is null", bufferedImage); 76 | assertTrue(bufferedImage.getHeight() > 0); 77 | assertTrue(bufferedImage.getWidth() > 0); 78 | } 79 | 80 | protected File createOutputFileName(String directory, File file, String format) { 81 | return createOutputFileName(directory, file.getName(), format); 82 | } 83 | 84 | protected File createOutputFileName(String directory, String fileName, String format) { 85 | 86 | File outputDir = new File(new File(new File(new File("./target"), "out"), this.moduleName), directory); 87 | 88 | if (!outputDir.exists()) { 89 | outputDir.mkdirs(); 90 | } 91 | 92 | return new File(outputDir, fileName + "." + format); 93 | } 94 | 95 | private File getImageDirectory() { 96 | return imageDirectory; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /code/jipsg/common/src/main/java/org/github/jipsg/common/image/BufferedImageFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.common.image; 18 | 19 | /** 20 | * ImageIO-based factory to handle BufferedImage. 21 | */ 22 | 23 | import javax.imageio.ImageIO; 24 | import java.awt.image.BufferedImage; 25 | import java.io.File; 26 | 27 | public class BufferedImageFactory { 28 | 29 | public static BufferedImage create(Object source) throws Exception { 30 | 31 | BufferedImage result; 32 | 33 | if (source instanceof File) { 34 | File sourceFile = (File) source; 35 | result = ImageIO.read(sourceFile); 36 | } else if (source instanceof String) { 37 | File sourceFile = new File(source.toString()); 38 | result = ImageIO.read(sourceFile); 39 | } else { 40 | throw new IllegalArgumentException("Don't know how to handle : " + source.getClass().getName()); 41 | } 42 | 43 | return result; 44 | } 45 | 46 | public static boolean writeBufferedImage(BufferedImage bufferedImage, String formatName, File file) throws Exception { 47 | System.out.println("Saving " + file.getPath()); 48 | return ImageIO.write(bufferedImage, formatName, file); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /code/jipsg/common/src/main/java/org/github/jipsg/common/image/BufferedImageOperations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.common.image; 18 | 19 | import org.github.jipsg.common.image.ops.AlphaChannelImageOp; 20 | 21 | import java.awt.image.BufferedImage; 22 | import java.awt.image.BufferedImageOp; 23 | import java.util.Collection; 24 | 25 | /** 26 | * Contains ready-to use image operations without additional dependencies. 27 | */ 28 | public class BufferedImageOperations { 29 | 30 | public static BufferedImage fillTransparentPixel(BufferedImage src) { 31 | 32 | // Since JDK 5 the JPG conversion messes up images with alpha-channels 33 | // therefore fill the alpha-channel with white pixels 34 | 35 | if (src.getColorModel().hasAlpha()) { 36 | return apply(src, new AlphaChannelImageOp()); 37 | } else { 38 | return src; 39 | } 40 | } 41 | 42 | public static BufferedImage apply(BufferedImage source, Collection bufferedImageOps) { 43 | BufferedImage result = source; 44 | 45 | for (BufferedImageOp bufferedImageOp : bufferedImageOps) { 46 | result = bufferedImageOp.filter(result, null); 47 | } 48 | 49 | return result; 50 | } 51 | 52 | public static BufferedImage apply(BufferedImage source, BufferedImageOp... bufferedImageOps) { 53 | BufferedImage result = source; 54 | 55 | for (BufferedImageOp bufferedImageOp : bufferedImageOps) { 56 | result = bufferedImageOp.filter(result, null); 57 | } 58 | 59 | return result; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /code/jipsg/common/src/main/java/org/github/jipsg/common/image/BufferedImageUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.common.image; 18 | 19 | import java.awt.Dimension; 20 | 21 | /** 22 | * Contains ready-to use image operations without additional dependencies. 23 | */ 24 | public class BufferedImageUtils { 25 | 26 | public static Dimension getScaledDimension(Dimension imgSize, Dimension boundary) { 27 | 28 | int original_width = imgSize.width; 29 | int original_height = imgSize.height; 30 | int bound_width = boundary.width; 31 | int bound_height = boundary.height; 32 | int new_width = original_width; 33 | int new_height = original_height; 34 | 35 | // first check if we need to scale width 36 | if (original_width > bound_width) { 37 | //scale width to fit 38 | new_width = bound_width; 39 | //scale height to maintain aspect ratio 40 | new_height = (new_width * original_height) / original_width; 41 | } 42 | 43 | // then check if we need to scale even with the new height 44 | if (new_height > bound_height) { 45 | //scale height to fit instead 46 | new_height = bound_height; 47 | //scale width to maintain aspect ratio 48 | new_width = (new_height * original_width) / original_height; 49 | } 50 | 51 | return new Dimension(new_width, new_height); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /code/jipsg/common/src/main/java/org/github/jipsg/common/image/ops/AlphaChannelImageOp.java: -------------------------------------------------------------------------------- 1 | package org.github.jipsg.common.image.ops; 2 | 3 | import java.awt.Color; 4 | import java.awt.Graphics2D; 5 | import java.awt.RenderingHints; 6 | import java.awt.geom.Point2D; 7 | import java.awt.geom.Rectangle2D; 8 | import java.awt.image.BufferedImage; 9 | import java.awt.image.BufferedImageOp; 10 | import java.awt.image.ColorModel; 11 | 12 | public class AlphaChannelImageOp implements BufferedImageOp { 13 | 14 | /** 15 | * Fill the alpha-channel with white pixels. 16 | */ 17 | public BufferedImage filter(BufferedImage src, BufferedImage dest) { 18 | BufferedImage result = new BufferedImage(src.getWidth(null), src.getHeight(null), BufferedImage.TYPE_INT_RGB); 19 | Graphics2D g = result.createGraphics(); 20 | g.drawImage(src, 0, 0, result.getWidth(), result.getHeight(), Color.WHITE, null); 21 | return result; 22 | } 23 | 24 | public Rectangle2D getBounds2D(BufferedImage src) { 25 | return null; 26 | } 27 | 28 | public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM) { 29 | return null; 30 | } 31 | 32 | public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { 33 | return null; 34 | } 35 | 36 | public RenderingHints getRenderingHints() { 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /code/jipsg/common/src/main/java/org/github/jipsg/common/image/ops/InvertImageOp.java: -------------------------------------------------------------------------------- 1 | package org.github.jipsg.common.image.ops; 2 | 3 | import java.awt.RenderingHints; 4 | import java.awt.geom.Point2D; 5 | import java.awt.geom.Rectangle2D; 6 | import java.awt.image.BufferedImage; 7 | import java.awt.image.BufferedImageOp; 8 | import java.awt.image.ColorModel; 9 | import java.awt.image.RescaleOp; 10 | 11 | public class InvertImageOp implements BufferedImageOp { 12 | 13 | public BufferedImage filter(BufferedImage src, BufferedImage dest) { 14 | RescaleOp op = new RescaleOp(-1.0f, 255f, null); 15 | return op.filter(src, dest); 16 | } 17 | 18 | public Rectangle2D getBounds2D(BufferedImage src) { 19 | return null; 20 | } 21 | 22 | public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM) { 23 | return null; 24 | } 25 | 26 | public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { 27 | return null; 28 | } 29 | 30 | public RenderingHints getRenderingHints() { 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /code/jipsg/image-manipulation/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | org.github.jipsg 7 | jipsg-image-manipulation 8 | 0.0.1-SNAPSHOT 9 | jipsg-image-manipulation 10 | 2014 11 | Java Image Manipulation 12 | 13 | 14 | 15 | org.github.jipsg 16 | jipsg-imageio 17 | ${project.version} 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /code/jipsg/image-manipulation/src/test/java/org/github/jipsg/image/comparison/IdenticalImageComparator.java: -------------------------------------------------------------------------------- 1 | package org.github.jipsg.image.comparison; 2 | 3 | import java.awt.Color; 4 | import java.awt.Graphics; 5 | import java.awt.image.BufferedImage; 6 | import java.io.IOException; 7 | 8 | /** 9 | * Compare two images to check if they are identical - based on Apache PDFBox. 10 | * See https://github.com/sgoeschl/java-image-processing-survival-guide/raw/master/slides/jipsg.pdf 11 | */ 12 | public class IdenticalImageComparator { 13 | 14 | /** 15 | * Get the difference between two images, identical colors are set to white, 16 | * differences are xored, the highest bit of each color is reset to avoid 17 | * colors that are too light 18 | * 19 | * @param bim1 20 | * @param bim2 21 | * @return If the images are different, the function returns a diff image If 22 | * the images are identical, the function returns null If the size is 23 | * different, a black border on the bottom and the right is created 24 | * @throws IOException 25 | */ 26 | public BufferedImage diffImages(BufferedImage bim1, BufferedImage bim2) throws IOException { 27 | int minWidth = Math.min(bim1.getWidth(), bim2.getWidth()); 28 | int minHeight = Math.min(bim1.getHeight(), bim2.getHeight()); 29 | int maxWidth = Math.max(bim1.getWidth(), bim2.getWidth()); 30 | int maxHeight = Math.max(bim1.getHeight(), bim2.getHeight()); 31 | BufferedImage bim3 = null; 32 | if (minWidth != maxWidth || minHeight != maxHeight) { 33 | bim3 = createEmptyDiffImage(minWidth, minHeight, maxWidth, maxHeight); 34 | } 35 | for (int x = 0; x < minWidth; ++x) { 36 | for (int y = 0; y < minHeight; ++y) { 37 | int rgb1 = bim1.getRGB(x, y); 38 | int rgb2 = bim2.getRGB(x, y); 39 | if (rgb1 != rgb2 40 | // don't bother about differences of 1 color step 41 | && (Math.abs((rgb1 & 0xFF) - (rgb2 & 0xFF)) > 1 42 | || Math.abs(((rgb1 >> 8) & 0xFF) - ((rgb2 >> 8) & 0xFF)) > 1 43 | || Math.abs(((rgb1 >> 16) & 0xFF) - ((rgb2 >> 16) & 0xFF)) > 1)) { 44 | if (bim3 == null) { 45 | bim3 = createEmptyDiffImage(minWidth, minHeight, maxWidth, maxHeight); 46 | } 47 | int r = Math.abs((rgb1 & 0xFF) - (rgb2 & 0xFF)); 48 | int g = Math.abs((rgb1 & 0xFF00) - (rgb2 & 0xFF00)); 49 | int b = Math.abs((rgb1 & 0xFF0000) - (rgb2 & 0xFF0000)); 50 | bim3.setRGB(x, y, 0xFFFFFF - (r | g | b)); 51 | } else { 52 | if (bim3 != null) { 53 | bim3.setRGB(x, y, Color.WHITE.getRGB()); 54 | } 55 | } 56 | } 57 | } 58 | return bim3; 59 | } 60 | 61 | /** 62 | * Create an image; the part between the smaller and the larger image is 63 | * painted black, the rest in white 64 | * 65 | * @param minWidth width of the smaller image 66 | * @param minHeight width of the smaller image 67 | * @param maxWidth height of the larger image 68 | * @param maxHeight height of the larger image 69 | * @return 70 | */ 71 | private BufferedImage createEmptyDiffImage(int minWidth, int minHeight, int maxWidth, int maxHeight) { 72 | BufferedImage bim3 = new BufferedImage(maxWidth, maxHeight, BufferedImage.TYPE_INT_RGB); 73 | Graphics graphics = bim3.getGraphics(); 74 | if (minWidth != maxWidth || minHeight != maxHeight) { 75 | graphics.setColor(Color.BLACK); 76 | graphics.fillRect(0, 0, maxWidth, maxHeight); 77 | } 78 | graphics.setColor(Color.WHITE); 79 | graphics.fillRect(0, 0, minWidth, minHeight); 80 | graphics.dispose(); 81 | return bim3; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /code/jipsg/image-manipulation/src/test/java/org/github/jipsg/image/comparison/IdenticalImageComparatorTest.java: -------------------------------------------------------------------------------- 1 | package org.github.jipsg.image.comparison; 2 | 3 | import org.github.jipsg.common.image.BufferedImageFactory; 4 | import org.github.jipsg.imageio.BaseImageIoTest; 5 | import org.junit.Test; 6 | 7 | import java.awt.image.BufferedImage; 8 | import java.io.File; 9 | 10 | import static org.junit.Assert.assertNull; 11 | 12 | /** 13 | * Created by sgoeschl on 18/11/14. 14 | */ 15 | public class IdenticalImageComparatorTest extends BaseImageIoTest { 16 | 17 | 18 | @Override 19 | public void setup() { 20 | super.setModuleName("image-processing"); 21 | super.setup(); 22 | } 23 | 24 | @Test 25 | public void testIdenticalImage() throws Exception { 26 | 27 | final BufferedImage bufferedImage1 = BufferedImageFactory.create("../../images/samples/open-office-01.pdf.300.0.png"); 28 | final BufferedImage bufferedImage2 = BufferedImageFactory.create("../../images/samples/open-office-01.pdf.300.0.png"); 29 | final IdenticalImageComparator identicalImageComparator = new IdenticalImageComparator(); 30 | assertNull(identicalImageComparator.diffImages(bufferedImage1, bufferedImage2)); 31 | } 32 | 33 | @Test 34 | public void testModifiedImage() throws Exception { 35 | 36 | final BufferedImage bufferedImage1 = BufferedImageFactory.create("../../images/samples/open-office-01.pdf.300.0.png"); 37 | final BufferedImage bufferedImage2 = BufferedImageFactory.create("../../images/samples/open-office-02.pdf.300.0.png"); 38 | final IdenticalImageComparator identicalImageComparator = new IdenticalImageComparator(); 39 | final BufferedImage bufferedImage = identicalImageComparator.diffImages(bufferedImage1, bufferedImage2); 40 | BufferedImageFactory.writeBufferedImage(bufferedImage, "png", new File("./target/indentical-modified.png")); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /code/jipsg/image-manipulation/src/test/java/org/github/jipsg/image/comparison/ImageComparator.java: -------------------------------------------------------------------------------- 1 | package org.github.jipsg.image.comparison; 2 | 3 | /** 4 | * Created by sgoeschl on 18/11/14. 5 | */ 6 | public interface ImageComparator { 7 | } 8 | -------------------------------------------------------------------------------- /code/jipsg/image-manipulation/src/test/java/org/github/jipsg/image/comparison/InvertedImageComparatorTest.java: -------------------------------------------------------------------------------- 1 | package org.github.jipsg.image.comparison; 2 | 3 | import org.github.jipsg.common.image.BufferedImageFactory; 4 | import org.github.jipsg.imageio.BaseImageIoTest; 5 | import org.junit.Test; 6 | 7 | import java.awt.image.BufferedImage; 8 | import java.io.File; 9 | 10 | /** 11 | * Created by sgoeschl on 18/11/14. 12 | */ 13 | public class InvertedImageComparatorTest extends BaseImageIoTest { 14 | 15 | 16 | @Override 17 | public void setup() { 18 | super.setModuleName("image-processing"); 19 | super.setup(); 20 | } 21 | 22 | @Test 23 | public void testIdenticalImage() throws Exception { 24 | 25 | final BufferedImage bufferedImage1 = BufferedImageFactory.create("../../images/samples/open-office-01.pdf.300.0.png"); 26 | final BufferedImage bufferedImage2 = BufferedImageFactory.create("../../images/samples/open-office-01.pdf.300.0.png"); 27 | final InvertingImageComparator imageComparator = new InvertingImageComparator(); 28 | final BufferedImage diffImages = imageComparator.compare(bufferedImage1, bufferedImage2); 29 | BufferedImageFactory.writeBufferedImage(diffImages, "png", new File("./target/inverted-identical.png")); 30 | } 31 | 32 | @Test 33 | public void testModifiedImage() throws Exception { 34 | 35 | final BufferedImage bufferedImage1 = BufferedImageFactory.create("../../images/samples/open-office-01.pdf.300.0.png"); 36 | final BufferedImage bufferedImage2 = BufferedImageFactory.create("../../images/samples/open-office-02.pdf.300.0.png"); 37 | final InvertingImageComparator imageComparator = new InvertingImageComparator(); 38 | final BufferedImage diffImages = imageComparator.compare(bufferedImage1, bufferedImage2); 39 | BufferedImageFactory.writeBufferedImage(diffImages, "png", new File("./target/inverted-modified.png")); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /code/jipsg/image-manipulation/src/test/java/org/github/jipsg/image/comparison/InvertingImageComparator.java: -------------------------------------------------------------------------------- 1 | package org.github.jipsg.image.comparison; 2 | 3 | import org.github.jipsg.common.image.ops.InvertImageOp; 4 | 5 | import java.awt.Color; 6 | import java.awt.Graphics2D; 7 | import java.awt.Image; 8 | import java.awt.Toolkit; 9 | import java.awt.image.BufferedImage; 10 | import java.awt.image.FilteredImageSource; 11 | import java.awt.image.ImageFilter; 12 | import java.awt.image.ImageProducer; 13 | import java.awt.image.RGBImageFilter; 14 | 15 | /** 16 | * Created by sgoeschl on 18/11/14. 17 | */ 18 | public class InvertingImageComparator { 19 | 20 | public BufferedImage compare(BufferedImage bim1, BufferedImage bim2) { 21 | 22 | // create an inverted image 23 | InvertImageOp invertImageOp = new InvertImageOp(); 24 | BufferedImage invertedImage = invertImageOp.filter(bim1, null); 25 | 26 | // make a transparent image using white 27 | BufferedImage transparentImage = createTransparentImage(bim2, Color.white); 28 | 29 | final BufferedImage finalImage = new BufferedImage(bim1.getWidth(), bim1.getHeight(), BufferedImage.TYPE_INT_ARGB); 30 | Graphics2D g = finalImage.createGraphics(); 31 | g.drawImage(invertedImage, 0, 0, null); 32 | g.drawImage(transparentImage, 0, 0, null); 33 | g.dispose(); 34 | 35 | return finalImage; 36 | } 37 | 38 | /** 39 | * Make provided image transparent wherever color matches the provided color. 40 | * 41 | * @param im BufferedImage whose color will be made transparent. 42 | * @param color Color in provided image which will be made transparent. 43 | * @return Image with transparency applied. 44 | */ 45 | public static BufferedImage createTransparentImage(final BufferedImage im, final Color color) { 46 | 47 | final ImageFilter filter = new RGBImageFilter() { 48 | // the color we are looking for (white)... Alpha bits are set to opaque 49 | public int markerRGB = color.getRGB() | 0xFFFFFFFF; 50 | 51 | public final int filterRGB(final int x, final int y, final int rgb) { 52 | if ((rgb | 0xFF000000) == markerRGB) { 53 | // Mark the alpha bits as zero - transparent 54 | return 0x00FFFFFF & rgb; 55 | } else { 56 | // nothing to do 57 | return rgb; 58 | } 59 | } 60 | }; 61 | 62 | final ImageProducer ip = new FilteredImageSource(im.getSource(), filter); 63 | final Image image = Toolkit.getDefaultToolkit().createImage(ip); 64 | 65 | final BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB); 66 | final Graphics2D g2 = bufferedImage.createGraphics(); 67 | g2.drawImage(image, 0, 0, null); 68 | g2.dispose(); 69 | return bufferedImage; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /code/jipsg/image-manipulation/src/test/java/org/github/jipsg/image/filter/AutoCorrectionFilter.java: -------------------------------------------------------------------------------- 1 | package org.github.jipsg.image.filter; 2 | 3 | import java.awt.RenderingHints; 4 | import java.awt.geom.Point2D; 5 | import java.awt.geom.Rectangle2D; 6 | import java.awt.image.BufferedImage; 7 | import java.awt.image.BufferedImageOp; 8 | import java.awt.image.ColorModel; 9 | import java.util.Arrays; 10 | 11 | /** 12 | * Copy & pasted from https://code.google.com/p/jalbum-autocorrect/source/browse/AutoCorrection/src/net/jalbum/filters/auto/AutoCorrectionFilter.java 13 | */ 14 | public class AutoCorrectionFilter implements BufferedImageOp { 15 | 16 | private final int AUTO_COLOR_VALUE = 20; 17 | private int[] pixels; 18 | private int[] lumPixels = new int[256]; 19 | private int[] redPixels = new int[256]; 20 | private int[] greenPixels = new int[256]; 21 | private int[] bluePixels = new int[256]; 22 | private double clipping = 0.001; 23 | private int[] largestOccs = new int[3]; 24 | private boolean contrast = true; 25 | private boolean levels = true; 26 | private boolean colors = true; 27 | private boolean colorsPossible = false; 28 | 29 | public Rectangle2D getBounds2D(BufferedImage src) { 30 | return null; 31 | } 32 | 33 | public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM) { 34 | return null; 35 | } 36 | 37 | public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { 38 | return null; 39 | } 40 | 41 | public RenderingHints getRenderingHints() { 42 | return null; 43 | } 44 | 45 | public BufferedImage filter(BufferedImage bi, BufferedImage dest) { 46 | int width = bi.getWidth(); 47 | int height = bi.getHeight(); 48 | pixels = new int[width * height]; 49 | bi.getRGB(0, 0, width, height, pixels, 0, width); 50 | BufferedImage dstBi = new BufferedImage(width, height, bi.getType()); 51 | 52 | if (colors) { 53 | autoColor(); 54 | } 55 | if (contrast) { 56 | autoContrast(); 57 | } 58 | if (levels) { 59 | autoLevels(); 60 | } 61 | 62 | dstBi.setRGB(0, 0, width, height, pixels, 0, width); 63 | 64 | return dstBi; 65 | } 66 | 67 | public void setContrast(Boolean contrast) { 68 | this.contrast = contrast; 69 | } 70 | 71 | public void setLevels(Boolean levels) { 72 | this.levels = levels; 73 | } 74 | 75 | public void setColors(boolean colors) { 76 | this.colors = colors; 77 | } 78 | 79 | public boolean isColorsPossible() { 80 | return colorsPossible; 81 | } 82 | 83 | 84 | /** 85 | * calculates the auto contrast 86 | */ 87 | private void autoContrast() { 88 | createLuminanceArray(); 89 | int clipNum = round(pixels.length * clipping); 90 | int max = getMaximum(clipNum, lumPixels); 91 | int min = getMinimum(clipNum, lumPixels); 92 | 93 | double contrast = 255.0 / (max - min); 94 | double brightness = 127.5 - ((max + min) / 2.0); 95 | 96 | changeContrastBrightness(contrast, brightness); 97 | 98 | } 99 | 100 | /** 101 | * searches for the maximum in the given array 102 | * 103 | * @param clipNum 104 | * @param arr 105 | * @return maximum 106 | */ 107 | private int getMaximum(int clipNum, int[] arr) { 108 | int max = 0; 109 | 110 | int i = 255; 111 | while (max < clipNum) { 112 | max += arr[i]; 113 | i--; 114 | } 115 | i++; 116 | max = i; 117 | 118 | return max; 119 | } 120 | 121 | /** 122 | * searches for the minimum in the given array 123 | * 124 | * @param clipNum 125 | * @param arr 126 | * @return minimum 127 | */ 128 | private int getMinimum(int clipNum, int[] arr) { 129 | int min = 0; 130 | 131 | int i = 0; 132 | while (min < clipNum) { 133 | min += arr[i]; 134 | i++; 135 | } 136 | i--; 137 | min = i; 138 | 139 | return min; 140 | } 141 | 142 | /** 143 | * adjust the auto contrast which means to change the brightness and contrast 144 | * 145 | * @param contrast 146 | * @param brightness 147 | */ 148 | private void changeContrastBrightness(double contrast, double brightness) { 149 | for (int i = 0; i < pixels.length; i++) { 150 | int argb = pixels[i]; 151 | 152 | int a = (argb >> 24) & 0xff; 153 | int r = (argb >> 16) & 0xff; 154 | int g = (argb >> 8) & 0xff; 155 | int b = argb & 0xff; 156 | 157 | double Y = 0.299 * r + 0.587 * g + 0.114 * b; // Calculates Luminance 158 | double Cb = -0.168736 * r - 0.331264 * g + 0.5 * b; 159 | double Cr = 0.5 * r - 0.418688 * g - 0.081312 * b; 160 | 161 | Y = contrast * (Y + brightness - 127.5) + 127.5; // Kontrast und Helligkeit wird verändert 162 | 163 | int rn = (int) (Y + 1.402 * Cr + 0.5); 164 | int gn = (int) (Y - 0.3441 * Cb - 0.7141 * Cr + 0.5); 165 | int bn = (int) (Y + 1.772 * Cb + 0.5); 166 | 167 | int[] clipped = clipping(rn, gn, bn); 168 | 169 | pixels[i] = (a << 24) | (clipped[0] << 16) | (clipped[1] << 8) | clipped[2]; 170 | } 171 | 172 | } 173 | 174 | /** 175 | * calculates the auto level values 176 | */ 177 | private void autoLevels() { 178 | createRedArray(); 179 | createGreenArray(); 180 | createBlueArray(); 181 | 182 | int clipNum = round(pixels.length * clipping); 183 | double[] factors = new double[6]; 184 | 185 | int max = getMaximum(clipNum, redPixels); 186 | int min = getMinimum(clipNum, redPixels); 187 | 188 | factors[0] = 255.0 / (max - min); //contrastR 189 | factors[1] = 127.5 - ((max + min) / 2.0); // brightnessR 190 | 191 | max = getMaximum(clipNum, greenPixels); 192 | min = getMinimum(clipNum, greenPixels); 193 | 194 | factors[2] = 255.0 / (max - min); //contrastG 195 | factors[3] = 127.5 - ((max + min) / 2.0); // brightnessG 196 | 197 | max = getMaximum(clipNum, bluePixels); 198 | min = getMinimum(clipNum, bluePixels); 199 | 200 | factors[4] = 255.0 / (max - min); //contrastB 201 | factors[5] = 127.5 - ((max + min) / 2.0); // brightnessB 202 | 203 | changeLevels(factors); 204 | 205 | } 206 | 207 | /** 208 | * adjusts the auto levels 209 | * 210 | * @param factors values for the adjustment 211 | */ 212 | private void changeLevels(double[] factors) { 213 | for (int i = 0; i < pixels.length; i++) { 214 | int argb = pixels[i]; 215 | 216 | int a = (argb >> 24) & 0xff; 217 | int r = (argb >> 16) & 0xff; 218 | int g = (argb >> 8) & 0xff; 219 | int b = argb & 0xff; 220 | 221 | int rn = (int) (factors[0] * (r + factors[1] - 127.5) + 127.5); 222 | int gn = (int) (factors[2] * (g + factors[3] - 127.5) + 127.5); 223 | int bn = (int) (factors[4] * (b + factors[5] - 127.5) + 127.5); 224 | 225 | int[] clipped = clipping(rn, gn, bn); 226 | 227 | pixels[i] = (a << 24) | (clipped[0] << 16) | (clipped[1] << 8) | clipped[2]; 228 | 229 | } 230 | } 231 | 232 | /** 233 | * adjusts auto color - move histogram to the left if it's cut at the right 234 | */ 235 | private void autoColor() { 236 | createRedArray(); 237 | createGreenArray(); 238 | createBlueArray(); 239 | 240 | int[] minIndices = new int[3]; 241 | Arrays.fill(minIndices, 0); 242 | colorsPossible = false; 243 | 244 | if (largestOccs[0] > (255 - AUTO_COLOR_VALUE)) { 245 | minIndices[0] = getMinIndex(redPixels); 246 | colorsPossible = true; 247 | } 248 | if (largestOccs[1] > (255 - AUTO_COLOR_VALUE)) { 249 | minIndices[1] = getMinIndex(greenPixels); 250 | colorsPossible = true; 251 | } 252 | if (largestOccs[2] > (255 - AUTO_COLOR_VALUE)) { 253 | minIndices[2] = getMinIndex(bluePixels); 254 | colorsPossible = true; 255 | } 256 | if (isColorsPossible()) { 257 | for (int i = 0; i < pixels.length; i++) { 258 | int argb = pixels[i]; 259 | 260 | int a = (argb >> 24) & 0xff; 261 | int r = (argb >> 16) & 0xff; 262 | int g = (argb >> 8) & 0xff; 263 | int b = argb & 0xff; 264 | 265 | r -= minIndices[0]; 266 | g -= minIndices[1]; 267 | b -= minIndices[2]; 268 | 269 | int[] clipped = clipping(r, g, b); 270 | 271 | pixels[i] = (a << 24) | (clipped[0] << 16) | (clipped[1] << 8) | clipped[2]; 272 | 273 | } 274 | } 275 | 276 | } 277 | 278 | /** 279 | * searches for the index of the first value 280 | * 281 | * @param arr 282 | * @return index 283 | */ 284 | private int getMinIndex(int[] arr) { 285 | int min = 0; 286 | int i = -1; 287 | while (min == 0) { 288 | i++; 289 | min = arr[i]; 290 | } 291 | if (i > AUTO_COLOR_VALUE) { 292 | i = AUTO_COLOR_VALUE; 293 | } 294 | return i; 295 | } 296 | 297 | /** 298 | * clipped the value when necessary (value should be between 0-255) 299 | * 300 | * @param r red 301 | * @param g green 302 | * @param b blue 303 | * @return clipped array with in it r, g, b 304 | */ 305 | private int[] clipping(int r, int g, int b) { 306 | 307 | if (r > 255) { 308 | r = 255; 309 | } else if (r < 0) { 310 | r = 0; 311 | } 312 | 313 | if (g > 255) { 314 | g = 255; 315 | } else if (g < 0) { 316 | g = 0; 317 | } 318 | 319 | if (b > 255) { 320 | b = 255; 321 | } else if (b < 0) { 322 | b = 0; 323 | } 324 | 325 | 326 | int[] clipped = {r, g, b}; 327 | return clipped; 328 | } 329 | 330 | /** 331 | * calculates the luminance values and counts them 332 | */ 333 | private void createLuminanceArray() { 334 | Arrays.fill(lumPixels, 0); 335 | 336 | for (int i = 0; i < pixels.length; i++) { 337 | int rgb = pixels[i]; 338 | 339 | int r = (rgb >> 16) & 0xff; 340 | int g = (rgb >> 8) & 0xff; 341 | int b = rgb & 0xff; 342 | 343 | double lum = 0.299 * r + 0.587 * g + 0.114 * b; // Calculates Luminance 344 | 345 | int lumR = round(lum); 346 | 347 | lumPixels[lumR]++; 348 | 349 | } 350 | 351 | } 352 | 353 | /** 354 | * counts occurrences of the 256 levels of red 355 | */ 356 | private void createRedArray() { 357 | Arrays.fill(redPixels, 0); 358 | int largestOcc = 0; 359 | int index = 0; 360 | 361 | for (int i = 0; i < this.pixels.length; i++) { 362 | int rgb = this.pixels[i]; 363 | 364 | int r = (rgb >> 16) & 0xff; 365 | 366 | redPixels[r]++; 367 | 368 | if (redPixels[r] > largestOcc) { 369 | largestOcc = redPixels[r]; 370 | index = r; 371 | } 372 | } 373 | largestOccs[0] = index; 374 | } 375 | 376 | /** 377 | * counts occurrences of the 256 levels of green 378 | */ 379 | private void createGreenArray() { 380 | Arrays.fill(greenPixels, 0); 381 | int largestOcc = 0; 382 | int index = 0; 383 | 384 | for (int i = 0; i < this.pixels.length; i++) { 385 | int rgb = this.pixels[i]; 386 | 387 | int g = (rgb >> 8) & 0xff; 388 | 389 | greenPixels[g]++; 390 | 391 | if (greenPixels[g] > largestOcc) { 392 | largestOcc = greenPixels[g]; 393 | index = g; 394 | } 395 | } 396 | largestOccs[1] = index; 397 | } 398 | 399 | /** 400 | * counts occurrences of the 256 levels of blue 401 | */ 402 | private void createBlueArray() { 403 | Arrays.fill(bluePixels, 0); 404 | int largestOcc = 0; 405 | int index = 0; 406 | 407 | for (int i = 0; i < this.pixels.length; i++) { 408 | int rgb = this.pixels[i]; 409 | 410 | int b = rgb & 0xff; 411 | 412 | bluePixels[b]++; 413 | 414 | if (bluePixels[b] > largestOcc) { 415 | largestOcc = bluePixels[b]; 416 | index = b; 417 | } 418 | } 419 | largestOccs[2] = index; 420 | } 421 | 422 | /** 423 | * Rounds double to int 424 | */ 425 | private int round(double lum) { 426 | int r = 0; 427 | 428 | if (lum >= 0) { 429 | r = (int) (lum + 0.5); 430 | } else { 431 | r = (int) (lum - 0.5); 432 | } 433 | return r; 434 | } 435 | 436 | } 437 | -------------------------------------------------------------------------------- /code/jipsg/image-manipulation/src/test/java/org/github/jipsg/image/manipulation/BaseImageManipulationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.image.manipulation; 18 | 19 | import org.github.jipsg.common.AbstractImageTest; 20 | import org.github.jipsg.common.image.BufferedImageUtils; 21 | 22 | import javax.imageio.ImageIO; 23 | import java.awt.Dimension; 24 | import java.awt.geom.AffineTransform; 25 | import java.awt.image.AffineTransformOp; 26 | import java.awt.image.BufferedImage; 27 | import java.io.File; 28 | import java.io.IOException; 29 | 30 | /** 31 | * Base class for testing Java ImageIO. 32 | */ 33 | public class BaseImageManipulationTest extends AbstractImageTest { 34 | 35 | @Override 36 | public void setup() { 37 | super.setModuleName("imageio"); 38 | super.setup(); 39 | } 40 | 41 | @Override 42 | public BufferedImage createBufferedImage(File file) throws IOException { 43 | return ImageIO.read(file); 44 | } 45 | 46 | @Override 47 | public void writeBufferedImage(BufferedImage bufferedImage, String formatName, File file) throws Exception { 48 | ImageIO.write(bufferedImage, formatName, file); 49 | } 50 | 51 | @Override 52 | public void writeBufferedImage(BufferedImage bufferedImage, float quality, int dpi, String formatName, File file) throws Exception { 53 | ImageIO.write(bufferedImage, formatName, file); 54 | } 55 | 56 | /** 57 | * Some quick and dirty image scaling - please note that for best performance 58 | * and quality you should use image rescaling libraries. 59 | */ 60 | @Override 61 | public BufferedImage resample(BufferedImage bufferedImage, int width, int height) { 62 | Dimension imageDimension = new Dimension(bufferedImage.getWidth(), bufferedImage.getHeight()); 63 | Dimension boundaryDimension = new Dimension(width, height); 64 | Dimension scaledDimension = BufferedImageUtils.getScaledDimension(imageDimension, boundaryDimension); 65 | 66 | double scaleX = scaledDimension.getWidth() / bufferedImage.getWidth(); 67 | double scaleY = scaledDimension.getHeight() / bufferedImage.getHeight(); 68 | 69 | AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY); 70 | AffineTransformOp biLinearScaleOp = new AffineTransformOp(scaleTransform, AffineTransformOp.TYPE_BILINEAR); 71 | 72 | return biLinearScaleOp.filter( 73 | bufferedImage, 74 | new BufferedImage(scaledDimension.width, scaledDimension.height, bufferedImage.getType())); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /code/jipsg/imageio/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | org.github.jipsg 7 | jipsg-imageio 8 | 0.0.1-SNAPSHOT 9 | jipsg-imageio 10 | 2014 11 | Java Image Processing Survival Guide ImageIO 12 | 13 | 14 | 15 | org.github.jipsg 16 | jipsg-common 17 | ${project.version} 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /code/jipsg/imageio/src/main/java/org/github/jipsg/imageio/BaseImageIoTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.imageio; 18 | 19 | import com.sun.imageio.plugins.jpeg.JPEGImageWriter; 20 | import org.github.jipsg.common.AbstractImageTest; 21 | import org.github.jipsg.common.image.BufferedImageUtils; 22 | import org.w3c.dom.Element; 23 | 24 | import javax.imageio.IIOImage; 25 | import javax.imageio.ImageIO; 26 | import javax.imageio.ImageTypeSpecifier; 27 | import javax.imageio.ImageWriteParam; 28 | import javax.imageio.metadata.IIOMetadata; 29 | import javax.imageio.plugins.jpeg.JPEGImageWriteParam; 30 | import javax.imageio.stream.ImageOutputStream; 31 | import java.awt.Dimension; 32 | import java.awt.geom.AffineTransform; 33 | import java.awt.image.AffineTransformOp; 34 | import java.awt.image.BufferedImage; 35 | import java.io.File; 36 | import java.io.FileOutputStream; 37 | import java.io.IOException; 38 | 39 | /** 40 | * Base class for testing Java ImageIO. 41 | */ 42 | public class BaseImageIoTest extends AbstractImageTest { 43 | 44 | @Override 45 | public void setup() { 46 | super.setModuleName("imageio"); 47 | super.setup(); 48 | } 49 | 50 | @Override 51 | public BufferedImage createBufferedImage(File file) throws IOException { 52 | return ImageIO.read(file); 53 | } 54 | 55 | @Override 56 | public void writeBufferedImage(BufferedImage bufferedImage, String formatName, File targetFile) throws Exception { 57 | System.out.println("Saving " + targetFile.getPath()); 58 | ImageIO.write(bufferedImage, formatName, targetFile); 59 | } 60 | 61 | @Override 62 | public void writeBufferedImage(BufferedImage bufferedImage, float quality, int dpi, String formatName, File targetFile) throws Exception { 63 | 64 | System.out.println("Saving " + targetFile.getPath()); 65 | 66 | if (formatName.equalsIgnoreCase("jpg") || formatName.equalsIgnoreCase("jpeg")) { 67 | JPEGImageWriter imageWriter = (JPEGImageWriter) ImageIO.getImageWritersBySuffix(formatName).next(); 68 | ImageWriteParam writeParam = imageWriter.getDefaultWriteParam(); 69 | ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB); 70 | IIOMetadata metadata = imageWriter.getDefaultImageMetadata(typeSpecifier, writeParam); 71 | 72 | if (formatName.equalsIgnoreCase("jpg") || formatName.equalsIgnoreCase("jpeg")) { 73 | Element tree = (Element) metadata.getAsTree("javax_imageio_jpeg_image_1.0"); 74 | Element jfif = (Element) tree.getElementsByTagName("app0JFIF").item(0); 75 | jfif.setAttribute("Xdensity", Integer.toString(dpi)); 76 | jfif.setAttribute("Ydensity", Integer.toString(dpi)); 77 | jfif.setAttribute("resUnits", "1"); 78 | metadata.setFromTree("javax_imageio_jpeg_image_1.0", tree); 79 | } 80 | 81 | if (quality >= 0 && quality <= 1f) { 82 | JPEGImageWriteParam jpegParams = (JPEGImageWriteParam) imageWriter.getDefaultWriteParam(); 83 | jpegParams.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT); 84 | jpegParams.setCompressionQuality(quality); 85 | } 86 | 87 | FileOutputStream os = new FileOutputStream(targetFile); 88 | final ImageOutputStream stream = ImageIO.createImageOutputStream(os); 89 | 90 | try { 91 | imageWriter.setOutput(stream); 92 | imageWriter.write(metadata, new IIOImage(bufferedImage, null, metadata), writeParam); 93 | } finally { 94 | stream.close(); 95 | } 96 | } else { 97 | writeBufferedImage(bufferedImage, formatName, targetFile); 98 | } 99 | } 100 | 101 | /** 102 | * Some quick and dirty image scaling - please note that for best performance 103 | * and quality you should use image rescaling libraries. 104 | */ 105 | @Override 106 | public BufferedImage resample(BufferedImage bufferedImage, int width, int height) { 107 | Dimension imageDimension = new Dimension(bufferedImage.getWidth(), bufferedImage.getHeight()); 108 | Dimension boundaryDimension = new Dimension(width, height); 109 | Dimension scaledDimension = BufferedImageUtils.getScaledDimension(imageDimension, boundaryDimension); 110 | 111 | double scaleX = scaledDimension.getWidth() / bufferedImage.getWidth(); 112 | double scaleY = scaledDimension.getHeight() / bufferedImage.getHeight(); 113 | 114 | AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY); 115 | AffineTransformOp biLinearScaleOp = new AffineTransformOp(scaleTransform, AffineTransformOp.TYPE_BILINEAR); 116 | 117 | return biLinearScaleOp.filter( 118 | bufferedImage, 119 | new BufferedImage(scaledDimension.width, scaledDimension.height, bufferedImage.getType())); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /code/jipsg/imageio/src/test/java/org/github/jipsg/imageio/ImageConversionImageIoTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.imageio; 18 | 19 | import org.junit.Before; 20 | import org.junit.Ignore; 21 | import org.junit.Test; 22 | 23 | import java.awt.Graphics2D; 24 | import java.awt.image.BufferedImage; 25 | import java.io.File; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | 29 | import static org.junit.Assert.assertEquals; 30 | import static org.junit.Assert.assertFalse; 31 | import static org.junit.Assert.assertTrue; 32 | 33 | /** 34 | * Load various images. 35 | */ 36 | public class ImageConversionImageIoTest extends BaseImageIoTest { 37 | 38 | @Before 39 | public void setup() { 40 | super.setup(); 41 | } 42 | 43 | // ====================================================================== 44 | // Image format conversion 45 | // ====================================================================== 46 | 47 | @Test 48 | public void testImageWriteAsJpeg() throws Exception { 49 | 50 | String formatName = "jpeg"; 51 | List sourceImageFileList = new ArrayList(); 52 | 53 | // sourceImageFileList.add(getImageFile("bmp", "marble.bmp")); 54 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 55 | // sourceImageFileList.add(getImageFile("jp2", "marble.jp2")); 56 | sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 57 | sourceImageFileList.add(getImageFile("png", "marble.png")); 58 | // sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 59 | 60 | for (File sourceImageFile : sourceImageFileList) { 61 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 62 | assertValidBufferedImage(bufferedImage); 63 | File targetImageFile = createOutputFileName("testImageWriteAsJpeg", sourceImageFile, formatName); 64 | writeBufferedImage(bufferedImage, formatName, targetImageFile); 65 | } 66 | } 67 | 68 | @Test 69 | public void testImageWriteAsPng() throws Exception { 70 | 71 | String formatName = "png"; 72 | List sourceImageFileList = new ArrayList(); 73 | 74 | // sourceImageFileList.add(getImageFile("bmp", "marble.bmp")); 75 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 76 | // sourceImageFileList.add(getImageFile("jp2", "marble.jp2")); 77 | sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 78 | sourceImageFileList.add(getImageFile("png", "marble.png")); 79 | // sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 80 | 81 | for (File sourceImageFile : sourceImageFileList) { 82 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 83 | assertValidBufferedImage(bufferedImage); 84 | File targetImageFile = createOutputFileName("testImageWriteAsPng", sourceImageFile, formatName); 85 | writeBufferedImage(bufferedImage, formatName, targetImageFile); 86 | } 87 | } 88 | 89 | // ====================================================================== 90 | // JPEG CMYK Images 91 | // ====================================================================== 92 | 93 | /** 94 | * Process the JPEGs with CMYK color space and store them as JPEG again. 95 | */ 96 | @Test 97 | @Ignore // javax.imageio.IIOException: Unsupported Image Type 98 | public void testProcessCMYKImages() throws Exception { 99 | 100 | String formatName = "jpeg"; 101 | List sourceImageFileList = new ArrayList(); 102 | 103 | sourceImageFileList.add(getImageFile("jpg", "test-image-cmyk-lzw.jpg")); 104 | sourceImageFileList.add(getImageFile("jpg", "test-image-cmyk-uncompressed.jpg")); 105 | 106 | for (File sourceImageFile : sourceImageFileList) { 107 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 108 | assertValidBufferedImage(bufferedImage); 109 | File targetImageFile = createOutputFileName("testProcessCMYKImages", sourceImageFile, formatName); 110 | writeBufferedImage(resample(bufferedImage, 320, 320), formatName, targetImageFile); 111 | } 112 | } 113 | 114 | // ====================================================================== 115 | // Transparent Images 116 | // ====================================================================== 117 | 118 | /** 119 | * Convert images having a transparency layer (alpha-channel) to JPG. Without 120 | * further handling the alpha-channel will be rendered black or as a red tint. 121 | */ 122 | @Test 123 | public void testWriteTransparentImagesAsJpeg() throws Exception { 124 | 125 | String formatName = "jpeg"; 126 | List sourceImageFileList = new ArrayList(); 127 | 128 | sourceImageFileList.add(getImageFile("gif", "test-image-transparent.gif")); 129 | sourceImageFileList.add(getImageFile("png", "test-image-transparent.png")); 130 | 131 | for (File sourceImageFile : sourceImageFileList) { 132 | 133 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 134 | assertValidBufferedImage(bufferedImage); 135 | assertTrue("Expecting transparency", bufferedImage.getColorModel().hasAlpha()); 136 | assertTrue("Expecting non-RGB color model", bufferedImage.getType() == BufferedImage.TYPE_4BYTE_ABGR || bufferedImage.getType() == BufferedImage.TYPE_BYTE_INDEXED); 137 | 138 | File targetImageFile = createOutputFileName("testWriteTransparentImagesAsJpeg", sourceImageFile, formatName); 139 | writeBufferedImage(bufferedImage, formatName, targetImageFile); 140 | } 141 | } 142 | 143 | /** 144 | * Convert images having a transparency layer (alpha-channel) to JPG. Remove 145 | * the alpha-channel (ARGB) by painting the image into an RGB image thereby 146 | * removing the fourth channel and transparency. 147 | */ 148 | @Test 149 | public void testWriteTransparentImagesUsingRGBAsJpeg() throws Exception { 150 | 151 | String formatName = "jpeg"; 152 | List sourceImageFileList = new ArrayList(); 153 | 154 | sourceImageFileList.add(getImageFile("gif", "test-image-transparent.gif")); 155 | sourceImageFileList.add(getImageFile("png", "test-image-transparent.png")); 156 | 157 | for (File sourceImageFile : sourceImageFileList) { 158 | 159 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 160 | assertValidBufferedImage(bufferedImage); 161 | assertTrue("Expecting transparency", bufferedImage.getColorModel().hasAlpha()); 162 | assertTrue("Expecting non-RGB color model", bufferedImage.getType() == BufferedImage.TYPE_4BYTE_ABGR || bufferedImage.getType() == BufferedImage.TYPE_BYTE_INDEXED); 163 | 164 | int width = bufferedImage.getWidth(); 165 | int height = bufferedImage.getHeight(); 166 | final int imageType = BufferedImage.TYPE_INT_RGB; 167 | BufferedImage rgbBufferedImage = new BufferedImage(width, height, imageType); 168 | Graphics2D graphics = rgbBufferedImage.createGraphics(); 169 | graphics.drawImage(bufferedImage, 0, 0, null); 170 | graphics.dispose(); 171 | assertValidBufferedImage(rgbBufferedImage); 172 | assertFalse("Expecting no transparency", rgbBufferedImage.getColorModel().hasAlpha()); 173 | assertEquals("Expecting RGB color model", BufferedImage.TYPE_INT_RGB, rgbBufferedImage.getType()); 174 | 175 | File targetImageFile = createOutputFileName("testWriteTransparentImagesUsingRGBAsJpeg", sourceImageFile, formatName); 176 | writeBufferedImage(rgbBufferedImage, formatName, targetImageFile); 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /code/jipsg/imageio/src/test/java/org/github/jipsg/imageio/ImageLoadImageIoTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.imageio; 18 | 19 | import org.junit.Before; 20 | import org.junit.Ignore; 21 | import org.junit.Test; 22 | 23 | import javax.imageio.ImageIO; 24 | import java.awt.Dimension; 25 | import java.awt.image.BufferedImage; 26 | import java.io.File; 27 | import java.util.ArrayList; 28 | import java.util.HashSet; 29 | import java.util.List; 30 | import java.util.Set; 31 | 32 | import static org.junit.Assert.assertEquals; 33 | 34 | /** 35 | * Load various images. 36 | */ 37 | public class ImageLoadImageIoTest extends BaseImageIoTest { 38 | 39 | @Before 40 | public void setup() { 41 | super.setup(); 42 | super.setModuleName("imageio"); 43 | } 44 | 45 | // ====================================================================== 46 | // General 47 | // ====================================================================== 48 | 49 | /** 50 | * List available image formats. 51 | *

52 | * see http://examples.javacodegeeks.com/desktop-java/jai/list-read-write-supported-image-formats/ 53 | */ 54 | @Test 55 | public void testListSupportedImageFormats() throws Exception { 56 | 57 | Set set = new HashSet(); 58 | 59 | // Get list of all informal format names understood by the current set of registered readers 60 | String[] formatNames = ImageIO.getReaderFormatNames(); 61 | 62 | for (String formatName : formatNames) { 63 | set.add(formatName.toLowerCase()); 64 | } 65 | System.out.println("Supported read formats: " + set); 66 | 67 | set.clear(); 68 | 69 | // Get list of all informal format names understood by the current set of registered writers 70 | formatNames = ImageIO.getWriterFormatNames(); 71 | 72 | for (String formatName : formatNames) { 73 | set.add(formatName.toLowerCase()); 74 | } 75 | System.out.println("Supported write formats: " + set); 76 | 77 | set.clear(); 78 | 79 | // Get list of all MIME types understood by the current set of registered readers 80 | formatNames = ImageIO.getReaderMIMETypes(); 81 | 82 | for (String formatName : formatNames) { 83 | set.add(formatName.toLowerCase()); 84 | } 85 | System.out.println("Supported read MIME types: " + set); 86 | 87 | set.clear(); 88 | 89 | // Get list of all MIME types understood by the current set of registered writers 90 | formatNames = ImageIO.getWriterMIMETypes(); 91 | 92 | for (String formatName : formatNames) { 93 | set.add(formatName.toLowerCase()); 94 | } 95 | System.out.println("Supported write MIME types: " + set); 96 | } 97 | 98 | // ====================================================================== 99 | // Load various image formats 100 | // ====================================================================== 101 | 102 | @Test 103 | public void testLoadVariousImageFormats() throws Exception { 104 | 105 | List sourceImageFileList = new ArrayList(); 106 | 107 | sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 108 | sourceImageFileList.add(getImageFile("png", "marble.png")); 109 | // sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 110 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 111 | // sourceImageFileList.add(getImageFile("jp2", "marble.jp2")); 112 | 113 | for (File sourceImageFile : sourceImageFileList) { 114 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 115 | assertValidBufferedImage(bufferedImage); 116 | } 117 | } 118 | 119 | // ====================================================================== 120 | // JPEG 121 | // ====================================================================== 122 | 123 | /** 124 | * Plain-vanilla JPEG 125 | */ 126 | @Test 127 | public void testLoadJPEGImage() throws Exception { 128 | assertValidBufferedImage(createBufferedImage(getImageFile("jpg", "test-image-rgb-01.jpg"))); 129 | } 130 | 131 | /** 132 | * CMYK color model is not supported and fails with "javax.imageio.IIOException: Unsupported Image Type" 133 | */ 134 | @Ignore 135 | @Test 136 | public void testLoadCMYKImage() throws Exception { 137 | assertValidBufferedImage(createBufferedImage(getImageFile("jpg", "test-image-cmyk-uncompressed.jpg"))); 138 | } 139 | 140 | // ====================================================================== 141 | // TIFF 142 | // ====================================================================== 143 | 144 | /** 145 | * Load a TIFF image with compression 2. Silently no buffered image 146 | * is loaded at call causing assertValidBufferedImage() to fail. 147 | */ 148 | @Test 149 | @Ignore 150 | public void testLoadTiffGrayWithCompression2() throws Exception { 151 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-gray-compression-type-2.tiff"))); 152 | } 153 | 154 | /** 155 | * Load a TIFF image with compression 3. Silently no buffered image 156 | * is loaded at call causing assertValidBufferedImage() to fail. 157 | */ 158 | @Test 159 | @Ignore 160 | public void testLoadTiffWithCompression3() throws Exception { 161 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-gray-compression-type-3.tiff"))); 162 | } 163 | 164 | /** 165 | * Load a TIFF image with compression 4. Silently no buffered image 166 | * is loaded at call causing assertValidBufferedImage() to fail. 167 | */ 168 | @Test 169 | @Ignore 170 | public void testLoadTiffWithCompression4() throws Exception { 171 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-gray-compression-type-4.tiff"))); 172 | } 173 | 174 | /** 175 | * Load a TIFF image with compression 4. Silently no buffered image 176 | * is loaded at call causing assertValidBufferedImage() to fail. 177 | */ 178 | @Test 179 | @Ignore 180 | public void testLoadTiffMultiPageGray() throws Exception { 181 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-multi-gray-compression-type-4.tiff"))); 182 | } 183 | 184 | /** 185 | * Load a TIFF image with compression LZW. Silently no buffered image 186 | * is loaded at call causing assertValidBufferedImage() to fail. 187 | */ 188 | @Test 189 | @Ignore 190 | public void testLoadTiffSingleCmykCompressionLzw() throws Exception { 191 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-cmyk-compression-lzw.tiff"))); 192 | } 193 | 194 | // ====================================================================== 195 | // Decompression Bombs 196 | // ====================================================================== 197 | 198 | @Test 199 | public void testLoadLargeImage() throws Exception { 200 | BufferedImage bufferedImage = createBufferedImage(getImageFile("bombs", "picture-1G-19000x19000.png")); 201 | Dimension dimension = new Dimension(bufferedImage.getWidth(), bufferedImage.getHeight()); 202 | assertEquals(19000, dimension.height); 203 | assertEquals(19000, dimension.width); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /code/jipsg/imageio/src/test/java/org/github/jipsg/imageio/ImageResamplingImageIoTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.imageio; 18 | 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | 22 | import java.awt.image.BufferedImage; 23 | import java.io.File; 24 | import java.math.BigDecimal; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | /** 29 | * Load various images. 30 | */ 31 | public class ImageResamplingImageIoTest extends BaseImageIoTest { 32 | 33 | private static final BigDecimal BD_1000 = new BigDecimal(1000); 34 | private static final BigDecimal CM_INCH_RATIO = new BigDecimal(0.0254); 35 | 36 | @Before 37 | public void setup() { 38 | super.setup(); 39 | } 40 | 41 | /** 42 | * Load various image types and re-sample them to 640 x 480. 43 | */ 44 | @Test 45 | public void testResamplingImagesAsJpeg() throws Exception { 46 | 47 | String formatName = "jpeg"; 48 | List sourceImageFileList = new ArrayList(); 49 | 50 | sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 51 | sourceImageFileList.add(getImageFile("png", "marble.png")); 52 | // not supported by Java ImageIO 53 | // sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 54 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 55 | 56 | for (File sourceImageFile : sourceImageFileList) { 57 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 58 | assertValidBufferedImage(bufferedImage); 59 | BufferedImage resampledBufferdImage = resample(bufferedImage, 640, 640); 60 | assertValidBufferedImage(resampledBufferdImage); 61 | File targetImageFile = createOutputFileName("testResamplingImagesAsJpeg", sourceImageFile, formatName); 62 | writeBufferedImage(resampledBufferdImage, formatName, targetImageFile); 63 | } 64 | } 65 | 66 | @Test 67 | public void testWriteImageWithQualityAndDpi() throws Exception { 68 | 69 | File targetImageFile; 70 | String formatName = "jpeg"; 71 | 72 | File sourceImageFile = getImageFile("jpg", "marble.jpg"); 73 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 74 | assertValidBufferedImage(bufferedImage); 75 | BufferedImage resampledBufferdImage = resample(bufferedImage, 640, 640); 76 | assertValidBufferedImage(resampledBufferdImage); 77 | 78 | // write as JPEG 79 | targetImageFile = createOutputFileName("testWriteImageWithQualityAndDpi", sourceImageFile, "jpg"); 80 | writeBufferedImage(resampledBufferdImage, 0.10f, 3145, formatName, targetImageFile); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /code/jipsg/jai/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | org.github.jipsg 7 | jipsg-jai 8 | 0.0.1-SNAPSHOT 9 | jipsg-jai 10 | 2014 11 | Java Image Processing Survival Guide JAI 12 | 13 | 14 | 15 | com.sun.media 16 | jai_core 17 | 1.1.3 18 | 19 | 20 | com.sun.media 21 | jai_codec 22 | 1.1.2_01 23 | 24 | 25 | com.sun.media 26 | jai_imageio 27 | 1.1 28 | 29 | 30 | org.github.jipsg 31 | jipsg-common 32 | ${project.version} 33 | test 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /code/jipsg/jai/src/test/java/org/github/jipsg/jai/AbstractJaiTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.jai; 18 | 19 | import com.sun.imageio.plugins.jpeg.JPEGImageWriter; 20 | import org.github.jipsg.common.AbstractImageTest; 21 | import org.github.jipsg.common.image.BufferedImageUtils; 22 | import org.w3c.dom.Element; 23 | 24 | import javax.imageio.IIOImage; 25 | import javax.imageio.ImageIO; 26 | import javax.imageio.ImageTypeSpecifier; 27 | import javax.imageio.ImageWriteParam; 28 | import javax.imageio.metadata.IIOMetadata; 29 | import javax.imageio.plugins.jpeg.JPEGImageWriteParam; 30 | import javax.imageio.stream.ImageOutputStream; 31 | import java.awt.Dimension; 32 | import java.awt.geom.AffineTransform; 33 | import java.awt.image.AffineTransformOp; 34 | import java.awt.image.BufferedImage; 35 | import java.io.File; 36 | import java.io.FileOutputStream; 37 | 38 | /** 39 | * Base class for testing Java Advanced Imaging. 40 | */ 41 | public class AbstractJaiTest extends AbstractImageTest { 42 | 43 | @Override 44 | public void setup() { 45 | super.setModuleName("jai"); 46 | super.setup(); 47 | } 48 | 49 | @Override 50 | public BufferedImage createBufferedImage(File file) throws Exception { 51 | return ImageIO.read(file); 52 | } 53 | 54 | @Override 55 | public void writeBufferedImage(BufferedImage bufferedImage, String formatName, File file) throws Exception { 56 | ImageIO.write(bufferedImage, formatName, file); 57 | } 58 | 59 | @Override 60 | public void writeBufferedImage(BufferedImage bufferedImage, float quality, int dpi, String formatName, File targetFile) throws Exception { 61 | 62 | if (formatName.equalsIgnoreCase("jpg") || formatName.equalsIgnoreCase("jpeg")) { 63 | JPEGImageWriter imageWriter = (JPEGImageWriter) ImageIO.getImageWritersBySuffix(formatName).next(); 64 | ImageWriteParam writeParam = imageWriter.getDefaultWriteParam(); 65 | ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB); 66 | IIOMetadata metadata = imageWriter.getDefaultImageMetadata(typeSpecifier, writeParam); 67 | 68 | if (formatName.equalsIgnoreCase("jpg") || formatName.equalsIgnoreCase("jpeg")) { 69 | 70 | Element tree = (Element) metadata.getAsTree("javax_imageio_jpeg_image_1.0"); 71 | Element jfif = (Element) tree.getElementsByTagName("app0JFIF").item(0); 72 | jfif.setAttribute("Xdensity", Integer.toString(dpi)); 73 | jfif.setAttribute("Ydensity", Integer.toString(dpi)); 74 | jfif.setAttribute("resUnits", "1"); 75 | metadata.setFromTree("javax_imageio_jpeg_image_1.0", tree); 76 | } 77 | 78 | if (quality >= 0 && quality <= 1f) { 79 | 80 | JPEGImageWriteParam jpegParams = (JPEGImageWriteParam) imageWriter.getDefaultWriteParam(); 81 | jpegParams.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT); 82 | jpegParams.setCompressionQuality(quality); 83 | 84 | } 85 | 86 | FileOutputStream os = new FileOutputStream(targetFile); 87 | final ImageOutputStream stream = ImageIO.createImageOutputStream(os); 88 | 89 | try { 90 | imageWriter.setOutput(stream); 91 | imageWriter.write(metadata, new IIOImage(bufferedImage, null, metadata), writeParam); 92 | } finally { 93 | stream.close(); 94 | } 95 | } else { 96 | writeBufferedImage(bufferedImage, formatName, targetFile); 97 | } 98 | } 99 | 100 | /** 101 | * Some quick and dirty image scaling - please note that for best performance 102 | * and quality you should use image rescaling libraries. 103 | */ 104 | @Override 105 | public BufferedImage resample(BufferedImage bufferedImage, int width, int height) { 106 | 107 | Dimension imageDimension = new Dimension(bufferedImage.getWidth(), bufferedImage.getHeight()); 108 | Dimension boundaryDimension = new Dimension(width, height); 109 | Dimension scaledDimension = BufferedImageUtils.getScaledDimension(imageDimension, boundaryDimension); 110 | 111 | double scaleX = scaledDimension.getWidth() / bufferedImage.getWidth(); 112 | double scaleY = scaledDimension.getHeight() / bufferedImage.getHeight(); 113 | 114 | AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY); 115 | AffineTransformOp biLinearScaleOp = new AffineTransformOp(scaleTransform, AffineTransformOp.TYPE_BILINEAR); 116 | 117 | return biLinearScaleOp.filter( 118 | bufferedImage, 119 | new BufferedImage(scaledDimension.width, scaledDimension.height, bufferedImage.getType())); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /code/jipsg/jai/src/test/java/org/github/jipsg/jai/ImageConversionJaiTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.jai; 18 | 19 | import org.junit.Before; 20 | import org.junit.Ignore; 21 | import org.junit.Test; 22 | 23 | import java.awt.Graphics2D; 24 | import java.awt.image.BufferedImage; 25 | import java.io.File; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | 29 | import static org.junit.Assert.assertEquals; 30 | import static org.junit.Assert.assertFalse; 31 | import static org.junit.Assert.assertTrue; 32 | 33 | /** 34 | * Load various images. 35 | */ 36 | public class ImageConversionJaiTest extends AbstractJaiTest { 37 | 38 | @Before 39 | public void setup() { 40 | super.setup(); 41 | super.setModuleName("jai"); 42 | } 43 | 44 | // ====================================================================== 45 | // Image format conversion 46 | // ====================================================================== 47 | 48 | @Test 49 | public void testWriteImageFormatsAsJpeg() throws Exception { 50 | 51 | String formatName = "jpeg"; 52 | List sourceImageFileList = new ArrayList(); 53 | 54 | sourceImageFileList.add(getImageFile("bmp", "marble.bmp")); 55 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 56 | sourceImageFileList.add(getImageFile("jp2", "marble.jp2")); 57 | sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 58 | sourceImageFileList.add(getImageFile("png", "marble.png")); 59 | sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 60 | 61 | for (File sourceImageFile : sourceImageFileList) { 62 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 63 | assertValidBufferedImage(bufferedImage); 64 | File targetImageFile = createOutputFileName("testWriteImageFormatsAsJpeg", sourceImageFile, formatName); 65 | writeBufferedImage(bufferedImage, formatName, targetImageFile); 66 | } 67 | } 68 | 69 | @Test 70 | public void testWriteImageFormatsAsPng() throws Exception { 71 | 72 | String formatName = "png"; 73 | List sourceImageFileList = new ArrayList(); 74 | 75 | sourceImageFileList.add(getImageFile("bmp", "marble.bmp")); 76 | sourceImageFileList.add(getImageFile("gif", "house-photo.gif")); 77 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 78 | sourceImageFileList.add(getImageFile("jp2", "marble.jp2")); 79 | sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 80 | sourceImageFileList.add(getImageFile("png", "marble.png")); 81 | sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 82 | 83 | for (File sourceImageFile : sourceImageFileList) { 84 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 85 | assertValidBufferedImage(bufferedImage); 86 | File targetImageFile = createOutputFileName("testWriteImageFormatsAsPng", sourceImageFile, formatName); 87 | writeBufferedImage(bufferedImage, formatName, targetImageFile); 88 | } 89 | } 90 | 91 | // ====================================================================== 92 | // JPEG CMYK Images 93 | // ====================================================================== 94 | 95 | /** 96 | * Process the JPEGs with CMYK color space and store them as JPEG again. 97 | */ 98 | @Test 99 | @Ignore // javax.imageio.IIOException: Unsupported Image Type 100 | public void testProcessCMYKImages() throws Exception { 101 | 102 | String formatName = "jpeg"; 103 | List sourceImageFileList = new ArrayList(); 104 | 105 | sourceImageFileList.add(getImageFile("jpg", "test-image-cmyk-lzw.jpg")); 106 | sourceImageFileList.add(getImageFile("jpg", "test-image-cmyk-uncompressed.jpg")); 107 | 108 | for (File sourceImageFile : sourceImageFileList) { 109 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 110 | assertValidBufferedImage(bufferedImage); 111 | File targetImageFile = createOutputFileName("testProcessCMYKImages", sourceImageFile, formatName); 112 | writeBufferedImage(resample(bufferedImage, 320, 320), formatName, targetImageFile); 113 | } 114 | } 115 | 116 | // ====================================================================== 117 | // Transparent Images 118 | // ====================================================================== 119 | 120 | /** 121 | * Convert images having a transparency layer (alpha-channel) to JPG. Without 122 | * further handling the alpha-channel will be rendered black or as a red tint. 123 | */ 124 | @Test 125 | public void testWriteTransparentImagesAsJpeg() throws Exception { 126 | 127 | String formatName = "jpeg"; 128 | List sourceImageFileList = new ArrayList(); 129 | 130 | sourceImageFileList.add(getImageFile("gif", "test-image-transparent.gif")); 131 | sourceImageFileList.add(getImageFile("png", "test-image-transparent.png")); 132 | 133 | for (File sourceImageFile : sourceImageFileList) { 134 | 135 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 136 | assertValidBufferedImage(bufferedImage); 137 | assertTrue("Expecting transparency", bufferedImage.getColorModel().hasAlpha()); 138 | assertTrue("Expecting non-RGB color model", bufferedImage.getType() == BufferedImage.TYPE_4BYTE_ABGR || bufferedImage.getType() == BufferedImage.TYPE_BYTE_INDEXED); 139 | 140 | File targetImageFile = createOutputFileName("testWriteTransparentImagesAsJpeg", sourceImageFile, formatName); 141 | writeBufferedImage(bufferedImage, formatName, targetImageFile); 142 | } 143 | } 144 | 145 | /** 146 | * Convert images having a transparency layer (alpha-channel) to JPG. Remove 147 | * the alpha-channel (ARGB) by painting the image into an RGB image thereby 148 | * removing the fourth channel and transparency. 149 | */ 150 | @Test 151 | public void testWriteTransparentImagesUsingRGBAsJpeg() throws Exception { 152 | 153 | String formatName = "jpeg"; 154 | List sourceImageFileList = new ArrayList(); 155 | 156 | sourceImageFileList.add(getImageFile("gif", "test-image-transparent.gif")); 157 | sourceImageFileList.add(getImageFile("png", "test-image-transparent.png")); 158 | 159 | for (File sourceImageFile : sourceImageFileList) { 160 | 161 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 162 | assertValidBufferedImage(bufferedImage); 163 | assertTrue("Expecting transparency", bufferedImage.getColorModel().hasAlpha()); 164 | assertTrue("Expecting non-RGB color model", bufferedImage.getType() == BufferedImage.TYPE_4BYTE_ABGR || bufferedImage.getType() == BufferedImage.TYPE_BYTE_INDEXED); 165 | 166 | BufferedImage rgbBufferedImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.TYPE_INT_RGB); 167 | Graphics2D graphics = rgbBufferedImage.createGraphics(); 168 | graphics.drawImage(bufferedImage, 0, 0, null); 169 | graphics.dispose(); 170 | assertValidBufferedImage(rgbBufferedImage); 171 | assertFalse("Expecting no transparency", rgbBufferedImage.getColorModel().hasAlpha()); 172 | assertEquals("Expecting RGB color model", BufferedImage.TYPE_INT_RGB, rgbBufferedImage.getType()); 173 | 174 | File targetImageFile = createOutputFileName("testWriteTransparentImagesUsingRGBAsJpeg", sourceImageFile, formatName); 175 | writeBufferedImage(rgbBufferedImage, formatName, targetImageFile); 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /code/jipsg/jai/src/test/java/org/github/jipsg/jai/ImageLoadJaiTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.jai; 18 | 19 | import org.junit.Before; 20 | import org.junit.Ignore; 21 | import org.junit.Test; 22 | 23 | import javax.imageio.ImageIO; 24 | import javax.imageio.ImageReader; 25 | import javax.imageio.stream.ImageInputStream; 26 | import java.awt.image.BufferedImage; 27 | import java.io.File; 28 | import java.util.ArrayList; 29 | import java.util.HashSet; 30 | import java.util.Iterator; 31 | import java.util.List; 32 | import java.util.Set; 33 | 34 | import static org.junit.Assert.assertEquals; 35 | 36 | 37 | /** 38 | * Load various images. 39 | */ 40 | public class ImageLoadJaiTest extends AbstractJaiTest { 41 | @Before 42 | public void setup() { 43 | super.setup(); 44 | this.setModuleName("jai"); 45 | } 46 | 47 | // ====================================================================== 48 | // General 49 | // ====================================================================== 50 | 51 | /** 52 | * List available image formats. 53 | * see http://examples.javacodegeeks.com/desktop-java/imageio/list-read-write-supported-image-formats/ 54 | */ 55 | @Test 56 | public void testListSupportedImageFormats() throws Exception { 57 | 58 | Set set = new HashSet(); 59 | 60 | // Get list of all informal format names understood by the current set of registered readers 61 | String[] formatNames = ImageIO.getReaderFormatNames(); 62 | 63 | for (int i = 0; i < formatNames.length; i++) { 64 | set.add(formatNames[i].toLowerCase()); 65 | } 66 | System.out.println("Supported read formats: " + set); 67 | 68 | set.clear(); 69 | 70 | // Get list of all informal format names understood by the current set of registered writers 71 | formatNames = ImageIO.getWriterFormatNames(); 72 | 73 | for (int i = 0; i < formatNames.length; i++) { 74 | set.add(formatNames[i].toLowerCase()); 75 | } 76 | System.out.println("Supported write formats: " + set); 77 | 78 | set.clear(); 79 | 80 | // Get list of all MIME types understood by the current set of registered readers 81 | formatNames = ImageIO.getReaderMIMETypes(); 82 | 83 | for (int i = 0; i < formatNames.length; i++) { 84 | set.add(formatNames[i].toLowerCase()); 85 | } 86 | System.out.println("Supported read MIME types: " + set); 87 | 88 | set.clear(); 89 | 90 | // Get list of all MIME types understood by the current set of registered writers 91 | formatNames = ImageIO.getWriterMIMETypes(); 92 | 93 | for (int i = 0; i < formatNames.length; i++) { 94 | set.add(formatNames[i].toLowerCase()); 95 | } 96 | System.out.println("Supported write MIME types: " + set); 97 | } 98 | 99 | // ====================================================================== 100 | // Load various image formats 101 | // ====================================================================== 102 | 103 | @Test 104 | public void testLoadVariousImageFormats() throws Exception { 105 | 106 | List sourceImageFileList = new ArrayList(); 107 | 108 | sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 109 | sourceImageFileList.add(getImageFile("png", "marble.png")); 110 | sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 111 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 112 | sourceImageFileList.add(getImageFile("jp2", "marble.jp2")); 113 | 114 | for (File sourceImageFile : sourceImageFileList) { 115 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 116 | assertValidBufferedImage(bufferedImage); 117 | } 118 | } 119 | 120 | // ====================================================================== 121 | // JPEG 122 | // ====================================================================== 123 | 124 | /** 125 | * Plain-vanilla JPEG 126 | */ 127 | @Test 128 | public void testLoadJPEGImage() throws Exception { 129 | assertValidBufferedImage(createBufferedImage(getImageFile("jpg", "test-image-rgb-01.jpg"))); 130 | } 131 | 132 | /** 133 | * CMYK color model is not supported. The test fails with "javax.imageio.IIOException: Unsupported Image Type" 134 | */ 135 | @Test 136 | @Ignore 137 | public void testLoadCMYKImage() throws Exception { 138 | assertValidBufferedImage(createBufferedImage(getImageFile("jpg", "test-image-cmyk-uncompressed.jpg"))); 139 | } 140 | 141 | // ====================================================================== 142 | // TIFF 143 | // ====================================================================== 144 | 145 | /** 146 | * Load a TIFF image with compression 2. 147 | */ 148 | @Test 149 | public void testLoadTiffGrayWithCompression2() throws Exception { 150 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-gray-compression-type-2.tiff"))); 151 | } 152 | 153 | /** 154 | * Load a TIFF image with compression 3. 155 | */ 156 | @Test 157 | public void testLoadTiffWithCompression3() throws Exception { 158 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-gray-compression-type-3.tiff"))); 159 | } 160 | 161 | /** 162 | * Load a single-page TIFF image with compression 4. 163 | */ 164 | @Test 165 | public void testLoadTiffWithCompression4() throws Exception { 166 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-gray-compression-type-4.tiff"))); 167 | } 168 | 169 | /** 170 | * Load a multi-page TIFF image with compression 4. 171 | */ 172 | @Test 173 | public void testLoadTiffMultiPageGray() throws Exception { 174 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-multi-gray-compression-type-4.tiff"))); 175 | } 176 | 177 | /** 178 | * Load a TIFF image with compression LZW. 179 | */ 180 | @Test 181 | public void testLoadTiffSingleCmykCompressionLzw() throws Exception { 182 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-cmyk-compression-lzw.tiff"))); 183 | } 184 | 185 | /** 186 | * Load a TIFF image with compression type 7 (JPEG). 187 | */ 188 | @Test 189 | public void testLoadTiffMultiRgbCompression7() throws Exception { 190 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-multi-rgb-compression-type-7.tiff"))); 191 | } 192 | 193 | // ====================================================================== 194 | // Multi-page TIFF extraction 195 | // ====================================================================== 196 | 197 | /** 198 | * Load a multi-page TIFF image and split it into its individual pages. 199 | */ 200 | @Test 201 | public void testExtractPagesFromMultiPageTiffCompression4() throws Exception { 202 | 203 | File sourceImageFile = getImageFile("tiff", "test-multi-gray-compression-type-4.tiff"); 204 | ImageInputStream is = ImageIO.createImageInputStream(sourceImageFile); 205 | 206 | // get the first matching reader 207 | Iterator iterator = ImageIO.getImageReaders(is); 208 | ImageReader imageReader = iterator.next(); 209 | imageReader.setInput(is); 210 | 211 | // split the multi-page TIFF 212 | int pages = imageReader.getNumImages(true); 213 | for (int i = 0; i < pages; i++) { 214 | BufferedImage bufferedImage = imageReader.read(i); 215 | assertValidBufferedImage(bufferedImage); 216 | } 217 | 218 | assertEquals("Expect to have 2 pages", 2, pages); 219 | } 220 | 221 | /** 222 | * Load a multi-page TIFF image and split it into its individual pages. 223 | */ 224 | @Test 225 | public void testExtractPagesFromMultiPageTiffCompression7() throws Exception { 226 | 227 | File sourceImageFile = getImageFile("tiff", "test-multi-rgb-compression-type-7.tiff"); 228 | ImageInputStream is = ImageIO.createImageInputStream(sourceImageFile); 229 | 230 | // get the first matching reader 231 | Iterator iterator = ImageIO.getImageReaders(is); 232 | ImageReader imageReader = iterator.next(); 233 | imageReader.setInput(is); 234 | 235 | // split the multi-page TIFF 236 | int pages = imageReader.getNumImages(true); 237 | for (int i = 0; i < pages; i++) { 238 | BufferedImage bufferedImage = imageReader.read(i); 239 | assertValidBufferedImage(bufferedImage); 240 | } 241 | 242 | assertEquals("Expect to have 10 pages", 10, pages); 243 | } 244 | 245 | } 246 | -------------------------------------------------------------------------------- /code/jipsg/jai/src/test/java/org/github/jipsg/jai/ImageResamplingJaiTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.jai; 18 | 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | 22 | import java.awt.image.BufferedImage; 23 | import java.io.File; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | /** 28 | * Load various images. 29 | */ 30 | public class ImageResamplingJaiTest extends AbstractJaiTest { 31 | 32 | @Before 33 | public void setup() { 34 | super.setup(); 35 | } 36 | 37 | /** 38 | * Load various image types and re-sample them to 640 x 480. 39 | */ 40 | @Test 41 | public void testResamplingImagesAsJpeg() throws Exception { 42 | 43 | String formatName = "jpeg"; 44 | List sourceImageFileList = new ArrayList(); 45 | 46 | sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 47 | sourceImageFileList.add(getImageFile("png", "marble.png")); 48 | // JAI fails with java.lang.IllegalArgumentException: Unknown image type 0 49 | // sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 50 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 51 | 52 | for (File sourceImageFile : sourceImageFileList) { 53 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 54 | assertValidBufferedImage(bufferedImage); 55 | BufferedImage resampledBufferedImage = resample(bufferedImage, 640, 480); 56 | assertValidBufferedImage(resampledBufferedImage); 57 | File targetImageFile = createOutputFileName("testResamplingImagesAsJpeg", sourceImageFile, formatName); 58 | writeBufferedImage(resampledBufferedImage, formatName, targetImageFile); 59 | } 60 | } 61 | 62 | @Test 63 | public void testWriteImageWithQualityAndDpi() throws Exception { 64 | 65 | File targetImageFile; 66 | String formatName = "jpeg"; 67 | 68 | File sourceImageFile = getImageFile("jpg", "marble.jpg"); 69 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 70 | assertValidBufferedImage(bufferedImage); 71 | BufferedImage resampledBufferdImage = resample(bufferedImage, 640, 640); 72 | assertValidBufferedImage(resampledBufferdImage); 73 | 74 | // write as JPEG 75 | targetImageFile = createOutputFileName("testWriteImageWithQualityAndDpi", sourceImageFile, "jpg"); 76 | writeBufferedImage(resampledBufferdImage, 0.10f, 123, formatName, targetImageFile); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /code/jipsg/pdfbox/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | org.github.jipsg 7 | jipsg-pdfbox 8 | 0.0.1-SNAPSHOT 9 | jipsg-pdfbox 10 | 2016 11 | Java Image Processing Survival Guide PdfBox 12 | 13 | 14 | 15 | org.github.jipsg 16 | jipsg-common 17 | ${project.version} 18 | test 19 | 20 | 21 | org.github.jipsg 22 | jipsg-imageio 23 | ${project.version} 24 | test 25 | 26 | 27 | org.apache.pdfbox 28 | pdfbox 29 | 1.8.12 30 | test 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /code/jipsg/pdfbox/src/test/java/org/github/jipsg/pdfbox/PdfBoxPreviewTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.pdfbox; 18 | 19 | import org.apache.pdfbox.pdmodel.PDDocument; 20 | import org.apache.pdfbox.pdmodel.PDPage; 21 | import org.apache.pdfbox.pdmodel.common.PDRectangle; 22 | import org.github.jipsg.imageio.BaseImageIoTest; 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | 26 | import java.awt.image.BufferedImage; 27 | import java.io.File; 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | import static java.awt.image.BufferedImage.TYPE_INT_RGB; 32 | import static org.junit.Assert.assertEquals; 33 | import static org.junit.Assert.assertFalse; 34 | import static org.junit.Assert.assertNotNull; 35 | 36 | /** 37 | * PDF manipulation code based on Apache PDFBox. 38 | */ 39 | public class PdfBoxPreviewTest extends BaseImageIoTest { 40 | 41 | private static final int DPI_72 = 72; 42 | private static final int START_PAGE = 1; 43 | private static final int LAST_PAGE = 16; 44 | 45 | @Before 46 | @Override 47 | public void setup() { 48 | super.setup(); 49 | setModuleName("pdfbox"); 50 | } 51 | 52 | @Test 53 | public void shouldCreatePdfPreviewImages() throws Exception { 54 | 55 | final int imageType = TYPE_INT_RGB; 56 | 57 | // final PDDocument pdDocument = PDDocument.load("./../../pdf/test-large-scan.pdf"); 58 | final PDDocument pdDocument = PDDocument.load("./../../pdf/erste-document-01.pdf"); 59 | 60 | final List images = toImages(pdDocument, START_PAGE, LAST_PAGE, DPI_72, imageType); 61 | 62 | assertNotNull(images); 63 | assertFalse(images.isEmpty()); 64 | assertEquals(images.get(0).getType(), imageType); 65 | 66 | for (int i = 0; i < images.size(); i++) { 67 | File targetImageFile = createOutputFileName("shouldCreatePdfPreviewImages", "page-" + i, "jpeg"); 68 | writeBufferedImage(images.get(i), "jpeg", targetImageFile); 69 | } 70 | } 71 | 72 | @SuppressWarnings("unchecked") 73 | public List toImages(PDDocument pdDocument, int startPage, int endPage, int resolution, int imageType) throws Exception { 74 | final List result = new ArrayList(); 75 | final List pages = pdDocument.getDocumentCatalog().getAllPages(); 76 | final int pagesSize = pages.size(); 77 | 78 | for (int i = startPage - 1; i < endPage && i < pagesSize; i++) { 79 | PDPage page = pages.get(i); 80 | PDRectangle cropBox = page.findCropBox(); 81 | float width = cropBox.getWidth(); 82 | float height = cropBox.getHeight(); 83 | int currResolution = calculateResolution(resolution, width, height); 84 | BufferedImage image = page.convertToImage(imageType, currResolution); 85 | 86 | if (image != null) { 87 | result.add(image); 88 | } 89 | } 90 | 91 | return result; 92 | } 93 | 94 | /** 95 | * Calculate the resolution being used assuming that the DPI is used 96 | * for an A4 page. 97 | */ 98 | protected int calculateResolution(int dpi, float cropBoxWidth, float cropBoxHeight) { 99 | int result; 100 | 101 | float maxPoints = Math.max(cropBoxWidth, cropBoxHeight); 102 | float pointForRequestedResolution = 29.7f * dpi / 2.54f; 103 | result = Math.round((pointForRequestedResolution * DPI_72 / maxPoints)); 104 | return result; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /code/jipsg/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | org.github.jipsg 7 | jipsg 8 | pom 9 | 0.0.1-SNAPSHOT 10 | jipsg 11 | 2014 12 | Java Image Processing Survival Guide JAI 13 | 14 | 15 | common 16 | imageio 17 | jai 18 | sanselan 19 | twelvemonkeys 20 | image-manipulation 21 | thumbnailator 22 | pdfbox 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /code/jipsg/readme.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/jipsg/readme.md -------------------------------------------------------------------------------- /code/jipsg/sanselan/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | org.github.jipsg 7 | jipsg-sanselan 8 | 0.0.1-SNAPSHOT 9 | jipsg-sanselan 10 | 2014 11 | Java Image Processing Survival Guide ImageIO 12 | 13 | 14 | 15 | org.github.jipsg 16 | jipsg-common 17 | ${project.version} 18 | test 19 | 20 | 21 | org.apache.commons 22 | commons-imaging 23 | 1.0-SNAPSHOT 24 | test 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /code/jipsg/sanselan/src/test/java/org/github/jipsg/sanselan/BaseSanselanTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.sanselan; 18 | 19 | import org.apache.commons.imaging.ImageFormat; 20 | import org.apache.commons.imaging.ImageFormats; 21 | import org.apache.commons.imaging.Imaging; 22 | import org.apache.commons.imaging.ImagingConstants; 23 | import org.github.jipsg.common.AbstractImageTest; 24 | import org.github.jipsg.common.image.BufferedImageUtils; 25 | 26 | import java.awt.Dimension; 27 | import java.awt.geom.AffineTransform; 28 | import java.awt.image.AffineTransformOp; 29 | import java.awt.image.BufferedImage; 30 | import java.io.File; 31 | import java.util.HashMap; 32 | import java.util.Map; 33 | 34 | /** 35 | * Base class for testing Apache Sanselan. 36 | */ 37 | public class BaseSanselanTest extends AbstractImageTest { 38 | 39 | @Override 40 | public void setup() { 41 | super.setModuleName("sanselan"); 42 | super.setup(); 43 | } 44 | 45 | @Override 46 | public BufferedImage createBufferedImage(File file) throws Exception { 47 | final Map params = new HashMap(); 48 | // set optional parameters if you like 49 | params.put(ImagingConstants.BUFFERED_IMAGE_FACTORY, 50 | new ManagedImageBufferedImageFactory()); 51 | // read image 52 | return Imaging.getBufferedImage(file, params); 53 | } 54 | 55 | @Override 56 | public void writeBufferedImage(final BufferedImage bufferedImage, final String formatName, final File file) throws Exception { 57 | final ImageFormat format = getImageFormat(formatName); 58 | final Map params = new HashMap(); 59 | Imaging.writeImage(bufferedImage, file, format, params); 60 | } 61 | 62 | @Override 63 | public void writeBufferedImage(BufferedImage bufferedImage, float quality, int dpi, String formatName, File file) throws Exception { 64 | writeBufferedImage(bufferedImage, formatName, file); 65 | } 66 | 67 | /** 68 | * Some quick and dirty image scaling - please note that for best performance 69 | * and quality you should use image rescaling libraries. 70 | */ 71 | @Override 72 | public BufferedImage resample(BufferedImage bufferedImage, int width, int height) { 73 | 74 | Dimension imageDimension = new Dimension(bufferedImage.getWidth(), bufferedImage.getHeight()); 75 | Dimension boundaryDimension = new Dimension(width, height); 76 | Dimension scaledDimension = BufferedImageUtils.getScaledDimension(imageDimension, boundaryDimension); 77 | 78 | double scaleX = scaledDimension.getWidth() / bufferedImage.getWidth(); 79 | double scaleY = scaledDimension.getHeight() / bufferedImage.getHeight(); 80 | 81 | AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY); 82 | AffineTransformOp biLinearScaleOp = new AffineTransformOp(scaleTransform, AffineTransformOp.TYPE_BILINEAR); 83 | 84 | return biLinearScaleOp.filter( 85 | bufferedImage, 86 | new BufferedImage(scaledDimension.width, scaledDimension.height, bufferedImage.getType())); 87 | } 88 | 89 | private ImageFormat getImageFormat(String formatName) { 90 | 91 | if ("jpg".equalsIgnoreCase(formatName)) { 92 | return ImageFormats.JPEG; 93 | } else if ("jpeg".equalsIgnoreCase(formatName)) { 94 | return ImageFormats.JPEG; 95 | } else if ("png".equalsIgnoreCase(formatName)) { 96 | return ImageFormats.PNG; 97 | } else { 98 | throw new IllegalArgumentException("Don't know how to handle : " + formatName); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /code/jipsg/sanselan/src/test/java/org/github/jipsg/sanselan/ImageConversionSanselanTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.sanselan; 18 | 19 | import org.junit.Before; 20 | import org.junit.Ignore; 21 | import org.junit.Test; 22 | 23 | import javax.imageio.ImageIO; 24 | import java.awt.Graphics2D; 25 | import java.awt.image.BufferedImage; 26 | import java.io.File; 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | import static org.junit.Assert.assertEquals; 31 | import static org.junit.Assert.assertFalse; 32 | import static org.junit.Assert.assertTrue; 33 | 34 | /** 35 | * Load various images. 36 | */ 37 | public class ImageConversionSanselanTest extends BaseSanselanTest { 38 | 39 | @Before 40 | public void setup() { 41 | super.setup(); 42 | } 43 | 44 | // ====================================================================== 45 | // Image format conversion 46 | // ====================================================================== 47 | 48 | @Test 49 | public void testWriteImageFormatsAsJpeg() throws Exception { 50 | 51 | String formatName = "jpeg"; 52 | List sourceImageFileList = new ArrayList(); 53 | 54 | // fails with org.apache.commons.imaging.ImageReadException: Invalid marker found in entropy data 55 | // sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 56 | 57 | sourceImageFileList.add(getImageFile("png", "marble.png")); 58 | sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 59 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 60 | sourceImageFileList.add(getImageFile("gif", "house-photo.gif")); 61 | 62 | for (File sourceImageFile : sourceImageFileList) { 63 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 64 | assertValidBufferedImage(bufferedImage); 65 | File targetImageFile = createOutputFileName("testWriteImageFormatsAsJpeg", sourceImageFile, formatName); 66 | ImageIO.write(bufferedImage, formatName, targetImageFile); 67 | } 68 | } 69 | 70 | @Test 71 | public void testWriteImageFormatsAsPng() throws Exception { 72 | 73 | String formatName = "png"; 74 | List sourceImageFileList = new ArrayList(); 75 | 76 | // sourceImageFileList.add(getImageFile("bmp", "marble.bmp")); 77 | sourceImageFileList.add(getImageFile("gif", "house-photo.gif")); 78 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 79 | // sourceImageFileList.add(getImageFile("jp2", "marble.jp2")); 80 | // fails with org.apache.commons.imaging.ImageReadException: Invalid marker found in entropy data 81 | // sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 82 | sourceImageFileList.add(getImageFile("png", "marble.png")); 83 | sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 84 | 85 | for (File sourceImageFile : sourceImageFileList) { 86 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 87 | assertValidBufferedImage(bufferedImage); 88 | File targetImageFile = createOutputFileName("testWriteImageFormatsAsPng", sourceImageFile, formatName); 89 | ImageIO.write(bufferedImage, formatName, targetImageFile); 90 | } 91 | } 92 | 93 | // ====================================================================== 94 | // JPEG CMYK Images 95 | // ====================================================================== 96 | 97 | /** 98 | * Process the JPEGs with CMYK color space and store them as JPEG again. 99 | */ 100 | @Test 101 | @Ignore // org.apache.commons.imaging.ImageReadException: 4 components are invalid or unsupported 102 | public void testProcessCMYKImages() throws Exception { 103 | 104 | String formatName = "jpeg"; 105 | List sourceImageFileList = new ArrayList(); 106 | 107 | sourceImageFileList.add(getImageFile("jpg", "test-image-cmyk-lzw.jpg")); 108 | sourceImageFileList.add(getImageFile("jpg", "test-image-cmyk-uncompressed.jpg")); 109 | 110 | for (File sourceImageFile : sourceImageFileList) { 111 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 112 | assertValidBufferedImage(bufferedImage); 113 | File targetImageFile = createOutputFileName("testProcessCMYKImages", sourceImageFile, formatName); 114 | writeBufferedImage(resample(bufferedImage, 320, 320), formatName, targetImageFile); 115 | } 116 | } 117 | 118 | // ====================================================================== 119 | // Transparent Images 120 | // ====================================================================== 121 | 122 | /** 123 | * Convert images having a transparency layer (alpha-channel) to JPG. Without 124 | * further handling the alpha-channel will be rendered black. 125 | * The test fails with "org.apache.commons.imaging.ImageWriteException: This image format (Jpeg-Custom) cannot be written." 126 | */ 127 | @Test(expected = org.apache.commons.imaging.ImageWriteException.class) 128 | public void testWriteTransparentImagesAsJpeg() throws Exception { 129 | 130 | String formatName = "jpeg"; 131 | List sourceImageFileList = new ArrayList(); 132 | 133 | sourceImageFileList.add(getImageFile("gif", "test-image-transparent.gif")); 134 | sourceImageFileList.add(getImageFile("png", "test-image-transparent.png")); 135 | 136 | for (File sourceImageFile : sourceImageFileList) { 137 | 138 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 139 | assertValidBufferedImage(bufferedImage); 140 | assertTrue("Expecting transparency", bufferedImage.getColorModel().hasAlpha()); 141 | assertTrue("Expecting ARGB color model", bufferedImage.getType() == BufferedImage.TYPE_INT_ARGB); 142 | 143 | File targetImageFile = createOutputFileName("testWriteTransparentImagesAsJpeg", sourceImageFile, formatName); 144 | writeBufferedImage(bufferedImage, formatName, targetImageFile); 145 | } 146 | } 147 | 148 | /** 149 | * Convert images having a transparency layer (alpha-channel) to JPG. Fill 150 | * the alpha-channel with Color.WHITE to have a useful image. 151 | * The test fails with "org.apache.commons.imaging.ImageWriteException: This image format (Jpeg-Custom) cannot be written." 152 | */ 153 | @Test(expected = org.apache.commons.imaging.ImageWriteException.class) 154 | public void testWriteTransparentImagesWithAlphaChannelHandlingAsJpeg() throws Exception { 155 | 156 | String formatName = "jpeg"; 157 | List sourceImageFileList = new ArrayList(); 158 | 159 | sourceImageFileList.add(getImageFile("gif", "test-image-transparent.gif")); 160 | sourceImageFileList.add(getImageFile("png", "test-image-transparent.png")); 161 | 162 | for (File sourceImageFile : sourceImageFileList) { 163 | 164 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 165 | assertValidBufferedImage(bufferedImage); 166 | assertTrue("Expecting transparency", bufferedImage.getColorModel().hasAlpha()); 167 | assertTrue("Expecting ARGB color model", bufferedImage.getType() == BufferedImage.TYPE_INT_ARGB); 168 | 169 | BufferedImage rgbBufferedImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.TYPE_INT_RGB); 170 | Graphics2D graphics = rgbBufferedImage.createGraphics(); 171 | graphics.drawImage(bufferedImage, 0, 0, null); 172 | graphics.dispose(); 173 | assertValidBufferedImage(rgbBufferedImage); 174 | assertFalse("Expecting no transparency", rgbBufferedImage.getColorModel().hasAlpha()); 175 | assertEquals("Expecting RGB color model", BufferedImage.TYPE_INT_RGB, rgbBufferedImage.getType()); 176 | 177 | File targetImageFile = createOutputFileName("testWriteTransparentImagesWithAlphaChannelHandlingAsJpeg", sourceImageFile, formatName); 178 | writeBufferedImage(bufferedImage, formatName, targetImageFile); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /code/jipsg/sanselan/src/test/java/org/github/jipsg/sanselan/ImageResamplingSanselanTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.sanselan; 18 | 19 | import org.junit.Before; 20 | import org.junit.Ignore; 21 | import org.junit.Test; 22 | 23 | import java.awt.image.BufferedImage; 24 | import java.io.File; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | /** 29 | * Load various images. 30 | */ 31 | public class ImageResamplingSanselanTest extends BaseSanselanTest { 32 | 33 | @Before 34 | public void setup() { 35 | super.setup(); 36 | } 37 | 38 | /** 39 | * Fails for unknown reasons with standard Sanselan code 40 | */ 41 | @Test 42 | @Ignore 43 | public void testResamplingImagesAsJpeg() throws Exception { 44 | 45 | String formatName = "jpeg"; 46 | List sourceImageFileList = new ArrayList(); 47 | 48 | // fails with org.apache.commons.imaging.ImageReadException: Invalid marker found in entropy data 49 | // sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 50 | sourceImageFileList.add(getImageFile("png", "marble.png")); 51 | sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 52 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 53 | 54 | for (File sourceImageFile : sourceImageFileList) { 55 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 56 | assertValidBufferedImage(bufferedImage); 57 | BufferedImage resampledBufferdImage = resample(bufferedImage, 640, 480); 58 | assertValidBufferedImage(resampledBufferdImage); 59 | File targetImageFile = createOutputFileName("testResamplingImagesAsJpeg", sourceImageFile, formatName); 60 | writeBufferedImage(resampledBufferdImage, formatName, targetImageFile); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /code/jipsg/sanselan/src/test/java/org/github/jipsg/sanselan/LoadImageSanselanTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.sanselan; 18 | 19 | import org.apache.commons.imaging.Imaging; 20 | import org.junit.Before; 21 | import org.junit.Ignore; 22 | import org.junit.Test; 23 | 24 | import java.awt.image.BufferedImage; 25 | import java.io.File; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | 29 | import static org.junit.Assert.assertEquals; 30 | 31 | /** 32 | * Load various images using Apache Commons Imaging. 33 | */ 34 | public class LoadImageSanselanTest extends BaseSanselanTest { 35 | 36 | @Before 37 | public void setup() { 38 | super.setup(); 39 | } 40 | 41 | /** 42 | * List available image formats. 43 | *

44 | * see http://examples.javacodegeeks.com/desktop-java/imageio/list-read-write-supported-image-formats/ 45 | */ 46 | @Test 47 | public void testListSupportedImageFormats() throws Exception { 48 | 49 | } 50 | 51 | // ====================================================================== 52 | // Load various image formats 53 | // ====================================================================== 54 | 55 | @Test 56 | public void testLoadVariousImageFormats() throws Exception { 57 | 58 | List sourceImageFileList = new ArrayList(); 59 | 60 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 61 | // fails with org.apache.commons.imaging.ImageReadException: Invalid marker found in entropy data 62 | // sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 63 | sourceImageFileList.add(getImageFile("png", "marble.png")); 64 | sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 65 | 66 | for (File sourceImageFile : sourceImageFileList) { 67 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 68 | assertValidBufferedImage(bufferedImage); 69 | } 70 | } 71 | 72 | // ====================================================================== 73 | // JPG 74 | // ====================================================================== 75 | 76 | /** 77 | * Plain-vanilla JPEG 78 | */ 79 | @Test 80 | @Ignore // org.apache.commons.imaging.ImageReadException: Invalid marker found in entropy data 81 | public void testLoadJPEGImage() throws Exception { 82 | assertValidBufferedImage(createBufferedImage(getImageFile("jpg", "test-image-rgb-01.jpg"))); 83 | } 84 | 85 | /** 86 | * CMYK color model is supported. 87 | */ 88 | @Test 89 | @Ignore // org.apache.commons.imaging.ImageReadException: 4 components are invalid or unsupported 90 | public void testLoadCMYKImage() throws Exception { 91 | assertValidBufferedImage(createBufferedImage(getImageFile("jpg", "test-image-cmyk-uncompressed.jpg"))); 92 | } 93 | 94 | // ====================================================================== 95 | // TIFF 96 | // ====================================================================== 97 | 98 | /** 99 | * Load a TIFF image with compression 2. 100 | * Expecting a "javax.imageio.IIOException: Unsupported TIFF Compression value: 2" but got 101 | * an "ArrayIndexOutOfBoundsException" 102 | */ 103 | @Test 104 | public void testLoadTiffGrayWithCompression2() throws Exception { 105 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-gray-compression-type-2.tiff"))); 106 | } 107 | 108 | /** 109 | * Load a TIFF image with compression 3. 110 | * Expecting a "javax.imageio.IIOException: Unsupported TIFF Compression value: 3" 111 | */ 112 | @Test 113 | public void testLoadTiffWithCompression3() throws Exception { 114 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-gray-compression-type-3.tiff"))); 115 | } 116 | 117 | /** 118 | * Load a TIFF image with compression 4. 119 | * Expecting a "javax.imageio.IIOException: Unsupported TIFF Compression value: 4" 120 | */ 121 | @Test 122 | public void testLoadTiffWithCompression4() throws Exception { 123 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-gray-compression-type-4.tiff"))); 124 | } 125 | 126 | /** 127 | * Load a TIFF image with compression 4. 128 | * Expecting a "javax.imageio.IIOException: Unsupported TIFF Compression value: 4" 129 | */ 130 | @Test 131 | public void testLoadTiffMultiPageGray() throws Exception { 132 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-multi-gray-compression-type-4.tiff"))); 133 | } 134 | 135 | /** 136 | * Load a TIFF image with compression LZW. 137 | */ 138 | @Test 139 | public void testLoadTiffSingleCmykCompressionLzw() throws Exception { 140 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-cmyk-compression-lzw.tiff"))); 141 | } 142 | 143 | /** 144 | * Load a TIFF image with compression type 7 (JPEG). 145 | */ 146 | @Ignore 147 | public void testLoadTiffMultiRgbCompression7() throws Exception { 148 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-multi-rgb-compression-type-7.tiff"))); 149 | } 150 | 151 | // ====================================================================== 152 | // Multi-page TIFF extraction 153 | // ====================================================================== 154 | 155 | /** 156 | * Load a multi-page TIFF image and split it into its individual pages. 157 | */ 158 | @Test 159 | public void testExtractPagesFromMultiPageTiff() throws Exception { 160 | 161 | File sourceImageFile = getImageFile("tiff", "test-multi-gray-compression-type-4.tiff"); 162 | 163 | List bufferedImageList = Imaging.getAllBufferedImages(sourceImageFile); 164 | 165 | for (BufferedImage bufferedImage : bufferedImageList) { 166 | assertValidBufferedImage(bufferedImage); 167 | } 168 | assertEquals("Expect to have 2 pages", 2, bufferedImageList.size()); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /code/jipsg/sanselan/src/test/java/org/github/jipsg/sanselan/ManagedImageBufferedImageFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.sanselan; 18 | 19 | import org.apache.commons.imaging.common.BufferedImageFactory; 20 | 21 | import java.awt.GraphicsConfiguration; 22 | import java.awt.GraphicsDevice; 23 | import java.awt.GraphicsEnvironment; 24 | import java.awt.Transparency; 25 | import java.awt.image.BufferedImage; 26 | 27 | /** 28 | * Created with IntelliJ IDEA. 29 | * User: sgoeschl 30 | * Date: 13/08/14 31 | * Time: 20:40 32 | * To change this template use File | Settings | File Templates. 33 | */ 34 | public class ManagedImageBufferedImageFactory implements 35 | BufferedImageFactory { 36 | 37 | public BufferedImage getColorBufferedImage(final int width, final int height, 38 | final boolean hasAlpha) { 39 | final GraphicsEnvironment ge = GraphicsEnvironment 40 | .getLocalGraphicsEnvironment(); 41 | final GraphicsDevice gd = ge.getDefaultScreenDevice(); 42 | final GraphicsConfiguration gc = gd.getDefaultConfiguration(); 43 | return gc.createCompatibleImage(width, height, 44 | Transparency.TRANSLUCENT); 45 | } 46 | 47 | public BufferedImage getGrayscaleBufferedImage(final int width, final int height, 48 | final boolean hasAlpha) { 49 | return getColorBufferedImage(width, height, hasAlpha); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /code/jipsg/thumbnailator/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | org.github.jipsg 7 | jipsg-thumbnailator 8 | 0.0.1-SNAPSHOT 9 | jipsg-thumbnailator 10 | 2014 11 | Java Image Processing Survival Guide Thumbnailator 12 | 13 | 14 | 15 | net.coobird 16 | thumbnailator 17 | 0.4.7 18 | 19 | 20 | org.github.jipsg 21 | jipsg-imageio 22 | ${project.version} 23 | test 24 | 25 | 26 | org.github.jipsg 27 | jipsg-common 28 | ${project.version} 29 | test 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /code/jipsg/thumbnailator/src/test/java/org/github/jipsg/thumbnailator/ImageScalingThumbnailatorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.thumbnailator; 18 | 19 | import junit.framework.TestCase; 20 | import net.coobird.thumbnailator.Thumbnails; 21 | import net.coobird.thumbnailator.resizers.configurations.Antialiasing; 22 | import org.github.jipsg.common.image.BufferedImageFactory; 23 | import org.junit.Test; 24 | 25 | import javax.imageio.ImageIO; 26 | import java.awt.image.BufferedImage; 27 | import java.io.File; 28 | 29 | /** 30 | * Image manipulation samples. 31 | */ 32 | public class ImageScalingThumbnailatorTest extends TestCase { 33 | 34 | public File createOutputFileName(String directory, String fileName, String format) { 35 | 36 | File outputDir = new File(new File(new File(new File("./target"), "out"), "thumbnailator"), directory); 37 | 38 | if (!outputDir.exists()) { 39 | outputDir.mkdirs(); 40 | } 41 | 42 | return new File(outputDir, fileName + "." + format); 43 | } 44 | 45 | 46 | public File createOutputFileName(String directory, File file, String format) { 47 | return createOutputFileName(directory, file.getName(), format); 48 | } 49 | 50 | // ====================================================================== 51 | // Image Scaling 52 | // ====================================================================== 53 | 54 | @Test 55 | public void testScaleImages() throws Exception { 56 | 57 | final int[] IMAGE_SCALE_DIMENSIONS = new int[] {575, 199, 80, 60}; 58 | final float QUALITY = 0.8f; 59 | 60 | File sourceImageDir = new File("../../images/willhaben"); 61 | File[] sourceImageFiles = sourceImageDir.listFiles(); 62 | 63 | long currentTime = System.currentTimeMillis(); 64 | 65 | for (File sourceImageFile : sourceImageFiles) { 66 | 67 | BufferedImage bufferedImage = ImageIO.read(sourceImageFile); 68 | 69 | for (int size : IMAGE_SCALE_DIMENSIONS) { 70 | 71 | BufferedImage scaledBufferedImage = Thumbnails.of(bufferedImage). 72 | width(size). 73 | height(size). 74 | keepAspectRatio(true). 75 | outputQuality(QUALITY). 76 | antialiasing(Antialiasing.ON). 77 | asBufferedImage(); 78 | 79 | bufferedImage = scaledBufferedImage; 80 | 81 | BufferedImageFactory.writeBufferedImage(scaledBufferedImage, "jpeg", createOutputFileName("testScaleImages/" + size, sourceImageFile, "jpeg")); 82 | } 83 | } 84 | 85 | long duration = System.currentTimeMillis() - currentTime; 86 | 87 | System.out.println("Scaling source images to " + IMAGE_SCALE_DIMENSIONS.length + " previews took " + duration / sourceImageFiles.length + " ms"); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /code/jipsg/twelvemonkeys/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | org.github.jipsg 7 | jipsg-twelvemonkeys 8 | 0.0.1-SNAPSHOT 9 | jipsg-twelvemonkeys 10 | 2014 11 | Java Image Processing Survival Guide Twelvemonkeys 12 | 13 | 14 | 15 | com.twelvemonkeys.imageio 16 | imageio-tiff 17 | 3.2.1 18 | 19 | 20 | com.twelvemonkeys.imageio 21 | imageio-jpeg 22 | 3.2.1 23 | 24 | 25 | com.twelvemonkeys.imageio 26 | imageio-core 27 | 3.2.1 28 | 29 | 30 | com.twelvemonkeys.imageio 31 | imageio-metadata 32 | 3.2.1 33 | 34 | 35 | com.twelvemonkeys.common 36 | common-image 37 | 3.2.1 38 | 39 | 40 | com.twelvemonkeys.common 41 | common-io 42 | 3.2.1 43 | 44 | 45 | com.twelvemonkeys.common 46 | common-lang 47 | 3.2.1 48 | 49 | 50 | org.github.jipsg 51 | jipsg-common 52 | ${project.version} 53 | test 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /code/jipsg/twelvemonkeys/src/test/java/org/github/jipsg/twelvemonkeys/BaseTwelveMonkeysTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.twelvemonkeys; 18 | 19 | import com.twelvemonkeys.image.ResampleOp; 20 | import org.github.jipsg.common.AbstractImageTest; 21 | import org.github.jipsg.common.image.BufferedImageUtils; 22 | 23 | import javax.imageio.ImageIO; 24 | import java.awt.Dimension; 25 | import java.awt.image.BufferedImage; 26 | import java.io.File; 27 | 28 | /** 29 | * Base class for testing TwelveMonkey library. 30 | */ 31 | public class BaseTwelveMonkeysTest extends AbstractImageTest { 32 | 33 | @Override 34 | public void setup() { 35 | super.setModuleName("twelvemonkeys"); 36 | super.setup(); 37 | } 38 | 39 | @Override 40 | public BufferedImage createBufferedImage(File file) throws Exception { 41 | return ImageIO.read(file); 42 | } 43 | 44 | @Override 45 | public void writeBufferedImage(BufferedImage bufferedImage, String formatName, File file) throws Exception { 46 | System.out.println("Saving " + file.getPath()); 47 | ImageIO.write(bufferedImage, formatName, file); 48 | } 49 | 50 | @Override 51 | public void writeBufferedImage(BufferedImage bufferedImage, float quality, int dpi, String formatName, File file) throws Exception { 52 | throw new UnsupportedOperationException(); 53 | } 54 | 55 | @Override 56 | public BufferedImage resample(BufferedImage bufferedImage, int width, int height) { 57 | Dimension imageDimension = new Dimension(bufferedImage.getWidth(), bufferedImage.getHeight()); 58 | Dimension boundaryDimension = new Dimension(width, height); 59 | Dimension scaledDimension = BufferedImageUtils.getScaledDimension(imageDimension, boundaryDimension); 60 | ResampleOp resampleOp = new ResampleOp(scaledDimension.width, scaledDimension.height); 61 | return resampleOp.filter(bufferedImage, null); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /code/jipsg/twelvemonkeys/src/test/java/org/github/jipsg/twelvemonkeys/ImageConversionTwelveMonkeysTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.twelvemonkeys; 18 | 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | 22 | import java.awt.Graphics2D; 23 | import java.awt.image.BufferedImage; 24 | import java.io.File; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | import static org.junit.Assert.assertEquals; 29 | import static org.junit.Assert.assertFalse; 30 | import static org.junit.Assert.assertTrue; 31 | 32 | /** 33 | * Load various images. 34 | */ 35 | public class ImageConversionTwelveMonkeysTest extends BaseTwelveMonkeysTest { 36 | 37 | @Before 38 | public void setup() { 39 | super.setup(); 40 | } 41 | 42 | // ====================================================================== 43 | // Image format conversion 44 | // ====================================================================== 45 | 46 | @Test 47 | public void testImageWriteAsJpeg() throws Exception { 48 | 49 | String formatName = "jpeg"; 50 | List sourceImageFileList = new ArrayList(); 51 | 52 | // sourceImageFileList.add(getImageFile("bmp", "marble.bmp")); 53 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 54 | // sourceImageFileList.add(getImageFile("jp2", "marble.jp2")); 55 | sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 56 | sourceImageFileList.add(getImageFile("png", "marble.png")); 57 | sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 58 | 59 | for (File sourceImageFile : sourceImageFileList) { 60 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 61 | assertValidBufferedImage(bufferedImage); 62 | File targetImageFile = createOutputFileName("testImageWriteAsJpeg", sourceImageFile, formatName); 63 | writeBufferedImage(bufferedImage, formatName, targetImageFile); 64 | } 65 | } 66 | 67 | @Test 68 | public void testImageWriteAsPng() throws Exception { 69 | 70 | String formatName = "png"; 71 | List sourceImageFileList = new ArrayList(); 72 | 73 | // sourceImageFileList.add(getImageFile("bmp", "marble.bmp")); 74 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 75 | // sourceImageFileList.add(getImageFile("jp2", "marble.jp2")); 76 | sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 77 | sourceImageFileList.add(getImageFile("png", "marble.png")); 78 | sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 79 | 80 | for (File sourceImageFile : sourceImageFileList) { 81 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 82 | assertValidBufferedImage(bufferedImage); 83 | File targetImageFile = createOutputFileName("testImageWriteAsPng", sourceImageFile, formatName); 84 | writeBufferedImage(bufferedImage, formatName, targetImageFile); 85 | } 86 | } 87 | 88 | // ====================================================================== 89 | // JPEG CMYK Images 90 | // ====================================================================== 91 | 92 | /** 93 | * Process the JPEGs with CMYK color space and store them as JPEG again. 94 | */ 95 | @Test 96 | public void testProcessCMYKImages() throws Exception { 97 | 98 | String formatName = "jpeg"; 99 | List sourceImageFileList = new ArrayList(); 100 | 101 | sourceImageFileList.add(getImageFile("jpg", "test-image-cmyk-lzw.jpg")); 102 | sourceImageFileList.add(getImageFile("jpg", "test-image-cmyk-uncompressed.jpg")); 103 | 104 | for (File sourceImageFile : sourceImageFileList) { 105 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 106 | assertValidBufferedImage(bufferedImage); 107 | File targetImageFile = createOutputFileName("testProcessCMYKImages", sourceImageFile, formatName); 108 | writeBufferedImage(resample(bufferedImage, 320, 320), formatName, targetImageFile); 109 | } 110 | } 111 | 112 | // ====================================================================== 113 | // Transparent Images 114 | // ====================================================================== 115 | 116 | /** 117 | * Convert images having a transparency layer (alpha-channel) to JPG. Without 118 | * further handling the alpha-channel will be rendered black or as a red tint. 119 | */ 120 | @Test 121 | public void testWriteTransparentImagesAsJpeg() throws Exception { 122 | 123 | String formatName = "jpeg"; 124 | List sourceImageFileList = new ArrayList(); 125 | 126 | sourceImageFileList.add(getImageFile("gif", "test-image-transparent.gif")); 127 | sourceImageFileList.add(getImageFile("png", "test-image-transparent.png")); 128 | 129 | for (File sourceImageFile : sourceImageFileList) { 130 | 131 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 132 | assertValidBufferedImage(bufferedImage); 133 | assertTrue("Expecting transparency", bufferedImage.getColorModel().hasAlpha()); 134 | assertTrue("Expecting non-RGB color model", bufferedImage.getType() == BufferedImage.TYPE_4BYTE_ABGR || bufferedImage.getType() == BufferedImage.TYPE_BYTE_INDEXED); 135 | 136 | File targetImageFile = createOutputFileName("testWriteTransparentImagesAsJpeg", sourceImageFile, formatName); 137 | writeBufferedImage(bufferedImage, formatName, targetImageFile); 138 | } 139 | } 140 | 141 | /** 142 | * Convert images having a transparency layer (alpha-channel) to JPG. Remove 143 | * the alpha-channel (ARGB) by painting the image into an RGB image thereby 144 | * removing the fourth channel and transparency. 145 | */ 146 | @Test 147 | public void testWriteTransparentImagesUsingRGBAsJpeg() throws Exception { 148 | 149 | String formatName = "jpeg"; 150 | List sourceImageFileList = new ArrayList(); 151 | 152 | sourceImageFileList.add(getImageFile("gif", "test-image-transparent.gif")); 153 | sourceImageFileList.add(getImageFile("png", "test-image-transparent.png")); 154 | 155 | for (File sourceImageFile : sourceImageFileList) { 156 | 157 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 158 | assertValidBufferedImage(bufferedImage); 159 | assertTrue("Expecting transparency", bufferedImage.getColorModel().hasAlpha()); 160 | assertTrue("Expecting non-RGB color model", bufferedImage.getType() == BufferedImage.TYPE_4BYTE_ABGR || bufferedImage.getType() == BufferedImage.TYPE_BYTE_INDEXED); 161 | 162 | BufferedImage rgbBufferedImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.TYPE_INT_RGB); 163 | Graphics2D graphics = rgbBufferedImage.createGraphics(); 164 | graphics.drawImage(bufferedImage, 0, 0, null); 165 | graphics.dispose(); 166 | assertValidBufferedImage(rgbBufferedImage); 167 | assertFalse("Expecting no transparency", rgbBufferedImage.getColorModel().hasAlpha()); 168 | assertEquals("Expecting RGB color model", BufferedImage.TYPE_INT_RGB, rgbBufferedImage.getType()); 169 | 170 | File targetImageFile = createOutputFileName("testWriteTransparentImagesUsingRGBAsJpeg", sourceImageFile, formatName); 171 | writeBufferedImage(rgbBufferedImage, formatName, targetImageFile); 172 | } 173 | } 174 | 175 | // ====================================================================== 176 | // TIFF Images 177 | // ====================================================================== 178 | 179 | @Test 180 | public void testConvertTiffImagesToJpeg() throws Exception { 181 | 182 | String formatName = "jpeg"; 183 | List sourceImageFileList = new ArrayList(); 184 | 185 | sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 186 | sourceImageFileList.add(getImageFile("tiff", "test-multi-gray-compression-type-4.tiff")); 187 | sourceImageFileList.add(getImageFile("tiff", "test-multi-rgb-compression-type-7.tiff")); 188 | // sourceImageFileList.add(getImageFile("tiff", "test-single-cmyk-compression-lzw.tiff")); 189 | sourceImageFileList.add(getImageFile("tiff", "test-single-gray-compression-lzw.tiff")); 190 | sourceImageFileList.add(getImageFile("tiff", "test-single-gray-compression-type-2.tiff")); 191 | sourceImageFileList.add(getImageFile("tiff", "test-single-gray-compression-type-3.tiff")); 192 | sourceImageFileList.add(getImageFile("tiff", "test-single-gray-compression-type-4.tiff")); 193 | sourceImageFileList.add(getImageFile("tiff", "test-single-rgb-uncompressed.tiff")); 194 | 195 | for (File sourceImageFile : sourceImageFileList) { 196 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 197 | assertValidBufferedImage(bufferedImage); 198 | File targetImageFile = createOutputFileName("testConvertTiffImageWriteToJpeg", sourceImageFile, formatName); 199 | writeBufferedImage(bufferedImage, formatName, targetImageFile); 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /code/jipsg/twelvemonkeys/src/test/java/org/github/jipsg/twelvemonkeys/ImageLoadTwelveMonkeysTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.twelvemonkeys; 18 | 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | 22 | import javax.imageio.ImageIO; 23 | import javax.imageio.ImageReader; 24 | import javax.imageio.stream.ImageInputStream; 25 | import java.awt.image.BufferedImage; 26 | import java.io.File; 27 | import java.util.ArrayList; 28 | import java.util.HashSet; 29 | import java.util.Iterator; 30 | import java.util.List; 31 | import java.util.Set; 32 | 33 | import static org.junit.Assert.assertEquals; 34 | 35 | /** 36 | * Load various images. 37 | */ 38 | public class ImageLoadTwelveMonkeysTest extends BaseTwelveMonkeysTest { 39 | 40 | @Before 41 | @Override 42 | public void setup() { 43 | super.setup(); 44 | } 45 | 46 | // ====================================================================== 47 | // General 48 | // ====================================================================== 49 | 50 | /** 51 | * List available image formats. 52 | *

53 | * see http://examples.javacodegeeks.com/desktop-java/imageio/list-read-write-supported-image-formats/ 54 | */ 55 | @Test 56 | public void testListSupportedImageFormats() throws Exception { 57 | 58 | Set set = new HashSet(); 59 | 60 | // Get list of all informal format names understood by the current set of registered readers 61 | String[] formatNames = ImageIO.getReaderFormatNames(); 62 | 63 | for (final String formatName : formatNames) { 64 | set.add(formatName.toLowerCase()); 65 | } 66 | System.out.println("Supported read formats: " + set); 67 | 68 | set.clear(); 69 | 70 | // Get list of all informal format names understood by the current set of registered writers 71 | formatNames = ImageIO.getWriterFormatNames(); 72 | 73 | for (final String formatName : formatNames) { 74 | set.add(formatName.toLowerCase()); 75 | } 76 | System.out.println("Supported write formats: " + set); 77 | 78 | set.clear(); 79 | 80 | // Get list of all MIME types understood by the current set of registered readers 81 | formatNames = ImageIO.getReaderMIMETypes(); 82 | 83 | for (final String formatName : formatNames) { 84 | set.add(formatName.toLowerCase()); 85 | } 86 | System.out.println("Supported read MIME types: " + set); 87 | 88 | set.clear(); 89 | 90 | // Get list of all MIME types understood by the current set of registered writers 91 | formatNames = ImageIO.getWriterMIMETypes(); 92 | 93 | for (final String formatName : formatNames) { 94 | set.add(formatName.toLowerCase()); 95 | } 96 | System.out.println("Supported write MIME types: " + set); 97 | } 98 | 99 | // ====================================================================== 100 | // Load various image formats 101 | // ====================================================================== 102 | 103 | @Test 104 | public void testLoadVariousImageFormats() throws Exception { 105 | 106 | List sourceImageFileList = new ArrayList(); 107 | 108 | sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 109 | sourceImageFileList.add(getImageFile("png", "marble.png")); 110 | sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 111 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 112 | // sourceImageFileList.add(getImageFile("jp2", "marble.jp2")); 113 | 114 | for (File sourceImageFile : sourceImageFileList) { 115 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 116 | assertValidBufferedImage(bufferedImage); 117 | } 118 | } 119 | 120 | // ====================================================================== 121 | // JPEG 122 | // ====================================================================== 123 | 124 | /** 125 | * Plain-vanilla JPEG 126 | */ 127 | @Test 128 | public void testLoadJPEGImage() throws Exception { 129 | assertValidBufferedImage(createBufferedImage(getImageFile("jpg", "test-image-rgb-01.jpg"))); 130 | } 131 | 132 | /** 133 | * CMYK color model is supported. 134 | */ 135 | @Test 136 | public void testLoadCMYKImage() throws Exception { 137 | assertValidBufferedImage(createBufferedImage(getImageFile("jpg", "test-image-cmyk-uncompressed.jpg"))); 138 | } 139 | 140 | // ====================================================================== 141 | // TIFF 142 | // ====================================================================== 143 | 144 | /** 145 | * Load a TIFF image with compression 2. 146 | */ 147 | @Test 148 | public void testLoadTiffGrayWithCompression2() throws Exception { 149 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-gray-compression-type-2.tiff"))); 150 | } 151 | 152 | /** 153 | * Load a TIFF image with compression 3. 154 | */ 155 | @Test 156 | public void testLoadTiffWithCompression3() throws Exception { 157 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-gray-compression-type-3.tiff"))); 158 | } 159 | 160 | /** 161 | * Load a TIFF image with compression 4. 162 | */ 163 | @Test 164 | public void testLoadTiffWithCompression4() throws Exception { 165 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-gray-compression-type-4.tiff"))); 166 | } 167 | 168 | /** 169 | * Load a multi-page TIFF. 170 | */ 171 | @Test 172 | public void testLoadTiffMultiPageGray() throws Exception { 173 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-multi-gray-compression-type-4.tiff"))); 174 | } 175 | 176 | /** 177 | * Load a TIFF image with compression type 7 (JPEG). 178 | */ 179 | @Test 180 | public void testLoadTiffMultiRgbCompression7() throws Exception { 181 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-multi-rgb-compression-type-7.tiff"))); 182 | } 183 | 184 | /** 185 | * Load a TIFF image with compression LZW. 186 | */ 187 | @Test 188 | public void testLoadTiffSingleCmykCompressionLzw() throws Exception { 189 | assertValidBufferedImage(createBufferedImage(getImageFile("tiff", "test-single-cmyk-compression-lzw.tiff"))); 190 | } 191 | 192 | // ====================================================================== 193 | // Multi-page TIFF extraction 194 | // ====================================================================== 195 | 196 | /** 197 | * Load a multi-page TIFF image and split it into its individual pages. 198 | */ 199 | @Test 200 | public void testExtractPagesFromMultiPageTiffCompression4() throws Exception { 201 | 202 | File sourceImageFile = getImageFile("tiff", "test-multi-gray-compression-type-4.tiff"); 203 | ImageInputStream is = ImageIO.createImageInputStream(sourceImageFile); 204 | 205 | // get the first matching reader 206 | Iterator iterator = ImageIO.getImageReaders(is); 207 | ImageReader imageReader = iterator.next(); 208 | imageReader.setInput(is); 209 | 210 | // split the multi-page TIFF 211 | int pages = imageReader.getNumImages(true); 212 | for (int i = 0; i < pages; i++) { 213 | BufferedImage bufferedImage = imageReader.read(i); 214 | assertValidBufferedImage(bufferedImage); 215 | } 216 | 217 | assertEquals("Expect to have 2 pages", 2, pages); 218 | } 219 | 220 | /** 221 | * Load a multi-page TIFF image and split it into its individual pages. 222 | */ 223 | @Test 224 | public void testExtractPagesFromMultiPageTiffCompression7() throws Exception { 225 | 226 | File sourceImageFile = getImageFile("tiff", "test-multi-rgb-compression-type-7.tiff"); 227 | ImageInputStream is = ImageIO.createImageInputStream(sourceImageFile); 228 | 229 | // get the first matching reader 230 | Iterator iterator = ImageIO.getImageReaders(is); 231 | ImageReader imageReader = iterator.next(); 232 | imageReader.setInput(is); 233 | 234 | // split the multi-page TIFF 235 | int pages = imageReader.getNumImages(true); 236 | for (int i = 0; i < pages; i++) { 237 | BufferedImage bufferedImage = imageReader.read(i); 238 | assertValidBufferedImage(bufferedImage); 239 | } 240 | 241 | assertEquals("Expect to have 10 pages", 10, pages); 242 | } 243 | 244 | } 245 | -------------------------------------------------------------------------------- /code/jipsg/twelvemonkeys/src/test/java/org/github/jipsg/twelvemonkeys/ImageResamplingTwelveMonkeysTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.twelvemonkeys; 18 | 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | 22 | import java.awt.image.BufferedImage; 23 | import java.io.File; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | /** 28 | * Load various images. 29 | */ 30 | public class ImageResamplingTwelveMonkeysTest extends BaseTwelveMonkeysTest { 31 | 32 | @Before 33 | public void setup() { 34 | super.setup(); 35 | } 36 | 37 | /** 38 | * Load various image types and re-sample them to 640 x 480. 39 | */ 40 | @Test 41 | public void testResamplingImagesAsJpeg() throws Exception { 42 | 43 | String formatName = "jpeg"; 44 | List sourceImageFileList = new ArrayList(); 45 | 46 | sourceImageFileList.add(getImageFile("jpg", "marble.jpg")); 47 | sourceImageFileList.add(getImageFile("png", "marble.png")); 48 | sourceImageFileList.add(getImageFile("tiff", "marble.tiff")); 49 | sourceImageFileList.add(getImageFile("gif", "marble.gif")); 50 | 51 | for (File sourceImageFile : sourceImageFileList) { 52 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 53 | assertValidBufferedImage(bufferedImage); 54 | BufferedImage resampledBufferdImage = resample(bufferedImage, 640, 480); 55 | assertValidBufferedImage(resampledBufferdImage); 56 | File targetImageFile = createOutputFileName("testResamplingImagesAsJpeg", sourceImageFile, formatName); 57 | writeBufferedImage(resampledBufferdImage, formatName, targetImageFile); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /code/jipsg/twelvemonkeys/src/test/java/org/github/jipsg/twelvemonkeys/ImageScalingTwelveMonkeysTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.github.jipsg.twelvemonkeys; 18 | 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | 22 | import java.awt.image.BufferedImage; 23 | import java.io.File; 24 | 25 | /** 26 | * Load various images. 27 | */ 28 | public class ImageScalingTwelveMonkeysTest extends BaseTwelveMonkeysTest { 29 | 30 | @Before 31 | public void setup() { 32 | super.setup(); 33 | } 34 | 35 | // ====================================================================== 36 | // Image Scaling 37 | // ====================================================================== 38 | 39 | @Test 40 | public void testScaleImages() throws Exception { 41 | 42 | final int[] IMAGE_SCALE_DIMENSIONS = new int[] {575, 199, 80, 60}; 43 | 44 | File sourceImageDir = new File("../../images/willhaben"); 45 | File[] sourceImageFiles = sourceImageDir.listFiles(); 46 | 47 | long currentTime = System.currentTimeMillis(); 48 | 49 | for (File sourceImageFile : sourceImageFiles) { 50 | 51 | BufferedImage bufferedImage = createBufferedImage(sourceImageFile); 52 | 53 | for (int size : IMAGE_SCALE_DIMENSIONS) { 54 | BufferedImage scaledBufferedImage = resample(bufferedImage, size, size); 55 | bufferedImage = scaledBufferedImage; 56 | writeBufferedImage(scaledBufferedImage, "jpeg", createOutputFileName("testScaleImages/" + size, sourceImageFile, "jpeg")); 57 | } 58 | } 59 | 60 | long duration = System.currentTimeMillis() - currentTime; 61 | 62 | System.out.println("Scaling one source image to " + IMAGE_SCALE_DIMENSIONS.length + " previews took " + duration / sourceImageFiles.length + " ms"); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /code/libs/commons-imaging-1.0-20160915.001141-89.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/libs/commons-imaging-1.0-20160915.001141-89.jar -------------------------------------------------------------------------------- /code/libs/jai_codec-1.1.2_01.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/libs/jai_codec-1.1.2_01.jar -------------------------------------------------------------------------------- /code/libs/jai_core-1.1.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/libs/jai_core-1.1.3.jar -------------------------------------------------------------------------------- /code/libs/jai_imageio-1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/libs/jai_imageio-1.1.jar -------------------------------------------------------------------------------- /code/libs/mvn-install-file.bat: -------------------------------------------------------------------------------- 1 | # Java Advanced Imaging (JAI) 2 | 3 | call mvn install:install-file -Dfile=jai_core-1.1.3.jar -Dversion=1.1.3 -DgroupId=com.sun.media -DartifactId=jai_core -Dpackaging=jar -DcreateChecksum=true -DgeneratePom=true 4 | call mvn install:install-file -Dfile=jai_codec-1.1.2_01.jar -Dversion=1.1.2_01 -DgroupId=com.sun.media -DartifactId=jai_codec -Dpackaging=jar -DcreateChecksum=true -DgeneratePom=true 5 | call mvn install:install-file -Dfile=jai_imageio-1.1.jar -Dversion=1.1 -DgroupId=com.sun.media -DartifactId=jai_imageio -Dpackaging=jar -DcreateChecksum=true -DgeneratePom=true 6 | call mvn install:install-file -Dfile=commons-imaging-1.0-20160915.001141-89.jar -Dversion=1.0-20160915.001141-89 -DgroupId=org.apache.commons -DartifactId=commons-imaging -Dpackaging=jar -DcreateChecksum=true -DgeneratePom=true 7 | -------------------------------------------------------------------------------- /code/libs/mvn-install-file.sh: -------------------------------------------------------------------------------- 1 | # Java Advanced Imaging (JAI) 2 | 3 | mvn install:install-file -Dfile=jai_core-1.1.3.jar -Dversion=1.1.3 -DgroupId=com.sun.media -DartifactId=jai_core -Dpackaging=jar -DcreateChecksum=true -DgeneratePom=true 4 | mvn install:install-file -Dfile=jai_codec-1.1.2_01.jar -Dversion=1.1.2_01 -DgroupId=com.sun.media -DartifactId=jai_codec -Dpackaging=jar -DcreateChecksum=true -DgeneratePom=true 5 | mvn install:install-file -Dfile=jai_imageio-1.1.jar -Dversion=1.1 -DgroupId=com.sun.media -DartifactId=jai_imageio -Dpackaging=jar -DcreateChecksum=true -DgeneratePom=true 6 | mvn install:install-file -Dfile=commons-imaging-1.0-20160915.001141-89.jar -Dversion=1.0-SNAPSHOT -DgroupId=org.apache.commons -DartifactId=commons-imaging -Dpackaging=jar -DcreateChecksum=true -DgeneratePom=true 7 | -------------------------------------------------------------------------------- /code/libs/readme.md: -------------------------------------------------------------------------------- 1 | This folder contains libraries not found in Maven Central which need to be imported manually -------------------------------------------------------------------------------- /code/pdf/erste-document-01.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/pdf/erste-document-01.pdf -------------------------------------------------------------------------------- /code/pdf/sigice9_172.CVISION.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/pdf/sigice9_172.CVISION.pdf -------------------------------------------------------------------------------- /code/pdf/test-large-scan.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/code/pdf/test-large-scan.pdf -------------------------------------------------------------------------------- /paper/images/alpha-channel-after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/paper/images/alpha-channel-after.png -------------------------------------------------------------------------------- /paper/images/alpha-channel-before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/paper/images/alpha-channel-before.png -------------------------------------------------------------------------------- /paper/images/house-after.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/paper/images/house-after.jpg -------------------------------------------------------------------------------- /paper/images/house-before.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/paper/images/house-before.gif -------------------------------------------------------------------------------- /paper/images/pdf-jbig2-image-result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/paper/images/pdf-jbig2-image-result.jpg -------------------------------------------------------------------------------- /paper/images/pdf-jbig2-image-source.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/paper/images/pdf-jbig2-image-source.jpg -------------------------------------------------------------------------------- /paper/images/willhaben-advert-desktop-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/paper/images/willhaben-advert-desktop-small.png -------------------------------------------------------------------------------- /paper/images/willhaben-advert-desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/paper/images/willhaben-advert-desktop.png -------------------------------------------------------------------------------- /paper/images/willhaben-advert-mobile-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/paper/images/willhaben-advert-mobile-small.png -------------------------------------------------------------------------------- /paper/images/willhaben-advert-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/paper/images/willhaben-advert-mobile.png -------------------------------------------------------------------------------- /slides/images/12-monkeys-505001e4d6467.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/slides/images/12-monkeys-505001e4d6467.png -------------------------------------------------------------------------------- /slides/images/TwelveMonkeys ImageIO_logo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/slides/images/TwelveMonkeys ImageIO_logo.pdf -------------------------------------------------------------------------------- /slides/images/challanger-launch-wikipedia.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/slides/images/challanger-launch-wikipedia.jpg -------------------------------------------------------------------------------- /slides/images/large-image-scan-pdf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/slides/images/large-image-scan-pdf.jpg -------------------------------------------------------------------------------- /slides/images/logo/bouvet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/slides/images/logo/bouvet.png -------------------------------------------------------------------------------- /slides/images/logo/willhaben.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/slides/images/logo/willhaben.png -------------------------------------------------------------------------------- /slides/images/pdf-jbig2-image-result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/slides/images/pdf-jbig2-image-result.jpg -------------------------------------------------------------------------------- /slides/images/pdf-jbig2-image-source.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/slides/images/pdf-jbig2-image-source.jpg -------------------------------------------------------------------------------- /slides/images/willhaben-advert-desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/slides/images/willhaben-advert-desktop.png -------------------------------------------------------------------------------- /slides/images/willhaben-advert-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/slides/images/willhaben-advert-mobile.png -------------------------------------------------------------------------------- /slides/jipsg.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/slides/jipsg.key -------------------------------------------------------------------------------- /slides/jipsg.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgoeschl/java-image-processing-survival-guide/3cde84bba0b8018d6fcf222f76d041b6b950baa7/slides/jipsg.pdf -------------------------------------------------------------------------------- /slides/twelvemonkeys-slides.txt: -------------------------------------------------------------------------------- 1 | The Twelvemonkeys ImageIO project was created to solve many of the problems or similar problems to those mentioned by Sigfried. 2 | 3 | 4 | 5 | It was started when I was working for a CMS (Conntent managment system) vendor [Escenic, but we might skip mentioning names], that created CMS solutions targeted for the media insdustry (newspapers, broadcast). It's a web-centric CMS, and thus needed rich media/image support. 6 | The initial version was created, because we (the web content management system I worked on at the time) needed support for more image formats. 7 | 8 | History 9 | 10 | 2005...ish 11 | 1.x version: 12 | - Java (prior to J2SE 1.4) had only limitted support for reading JPEG, PNG and GIF 13 | - And more importantly, no official API for writing images existed. 14 | - The APIs that existed was based- around the java.awt.Image class, and not the more versatile BufferedImage class. 15 | 16 | - JMagick had support for many formats, but had no stream support, wich is very bad when working with web architectures. Temporary writing to disk increases the response time, and slows down overall performance. Plus the lack of binary compatibiltiy from version to version and being a nightmare to install. 17 | - JAI was around, but at the time reading/writing images using JAI requrired a different API. 18 | - None of the libraries had proper support for PSD format (JMagick didn't support reading individual layers) 19 | 20 | The initial version had a simple read/write API, that dealt with BufferedImages. 21 | 22 | 2008..ish 23 | 2.x version: 24 | Nowadays, the world is a little different, thus the goals have changed: 25 | 26 | - ImageIO (javax.imageio package) has become the standard API to read/write images in Java. 27 | - Thus the goal has been to help the Java platform read as many formats, as complete as possible, using the ImageIO API. 28 | - JAI ImageIO has rich support for formats: 29 | - However, it has bugs, and when you hit them: No-one listens. Seems to have no support from Oracle. 30 | - No official updates for the last years (last release was ...) 31 | - Dying community for the same reasons 32 | - Requires native libraries for full format support/best performance. Which means more difficult installation. And worse, no native libraries exist for many modern popular architectures (ie, 64 bit architectures) 33 | - License issues (http://stackoverflow.com/questions/23166231/java-advanced-imaging-license). 34 | - Some parts seems to be open source, some parts not (like the native code) 35 | - Multiple, semi-overlapping forks (GitHub, Google code, private) with the same + its own license issues... 36 | 37 | - JMagick hasn't changed much. TwelveMonkeys has wrapper readers/writers to allow users to use ImageMagicks rich format support, while using the ImageIO API. However, due to the nature of the library, it will never have the same performance or rich features. 38 | 39 | - Apache Commons Imaging has emerged from Sanselan. A quite mature library, but unfortunately has its own API. Combined with the the fact that it doesn't support all the same formats as ImageIO, this means you either have to program against multiple APIs, create your own wrappers or even your own abstraction API on top of these multiple APIs. 40 | 41 | - Conclusion: We need something better! We deserver better. :-) 42 | 43 | 3.0 version (current): 44 | - To be released "very soon" (development/pre-release versions has been in use at customer sites for 1-2 years) 45 | - Very much improved JPEG (read) support (CMYK handling, improved color profile handling, JFIF and EXIF support) over the standard JPEGImageReader that comes with the (Oracle) JRE. 46 | - Solves most of the issues that usually crops up at StackOverflow, "Exception reading JPEG with colorspace X", "Inconsistent metadata when reading JPEG" etc. 47 | - Full TIFF baseline support + most de facto standards + many popular extensions (read-only for now) 48 | - Support for CMYK color space and proper ICC profile based CMYK to RGB conversion support throughout the project 49 | 50 | 3.1 [this is all unofficial and its perhaps becoming too much "commercial"-like, so I'm fine with skipping this part]: 51 | - CMYK JPEG write support, better JPEG metadata support 52 | - TIFF write support, TIFF metadata support 53 | - PNM read/write, metadata support 54 | - CR2 support (limited read, thumbnail), TIFF/EXIF metadata 55 | 56 | 57 | 58 | 59 | 60 | TwevelMonkeys JPEG plug-in in aprticular: 61 | Goal: Read everything that can be read by other software*. 62 | 63 | Currently, not doing too bad. 64 | - However, because the base JPEG decoding is done by the same JNI code that the standard Java JPEGImageReader uses, we only support Huffman encoded, lossy JPEG, either baseline or progressive (ie, no Arithmetic encoding, no lossless compression). I guess this covers around 90% of all JPEGs in the known universe. 65 | 66 | *) Other software here, typically means libJPEG, but also Adobe Photoshop and others. 67 | 68 | 69 | - Memory mapped buffers, can they be used of anyting? 70 | 71 | 72 | 73 | Some thoughts and questions I have no answer for (and might be off-topic :-): 74 | - What about the future of Java and JavaFX? 75 | - JavaFX has its own Image class, can be converted from BufferedImage using some Swing/FX brigde utilities, but costly... Seems to be no direct replacement for ImageIO in the JavaFX world. Do we have to start from scratch? Again? 76 | 77 | 78 | Of course not: FX-IIO project started on GitHub. Acts as a bridge to allow creating BufferedImages backed by native JavaFX Image/WritableImage. 79 | 80 | 81 | Future development: 82 | - Mostly done: 83 | - PNM format (PBM, PGM, PPM and PAM) 84 | - Read + write + metadata 85 | - PCX, TGA and SGI formats 86 | - Read-only support + metadata 87 | 88 | - Work in progress: More complete TIFF image read and write support 89 | - Read support for FAX compressions (CCITT T4 and T6) 90 | - Write support 91 | - LZW compression as well as all baseline compressions 92 | - JPEG (Compresssion: 7, aka New-style JPEG) 93 | - Tiles/Strips 94 | - Metadata 95 | 96 | - Work in progress: Camera RAW support 97 | - TIFF/EP + DNG support (Adobe Digital Negative) 98 | - NEF support (Nikon Electronic File) 99 | - CR2 support (Cannon RAW file) 100 | - Currently able to extract all non-compressed and lossy JPEG data previews + thumbnails 101 | (Actually easier than I thought, as all camera RAW formats are Exif-based, meaning they are basically a TIFF structure) 102 | 103 | - Work in progress: Pure Java JPEG lossless + arithmetic decoder (+encoder?) 104 | - Needed for Camera RAW plugins 105 | - Most RAW files uses lossless JPEG encoding 106 | 107 | -------------------------------------------------------------------------------- /slides/twelvemonkeys-slides2.txt: -------------------------------------------------------------------------------- 1 | TwelveMonkeys ImageIO 2 | 3 | What is it: 4 | 5 | Mainly: A collection of plug-ins for Java's ImageIO. 6 | - Expands the number of formats supported 7 | - Supports variants of formats not supported by the JRE (ie. JPEG) 8 | 9 | Also: 10 | - Extension for deploying ImageIO plugins in web app 11 | - Basic support for on-demand dynamic image processing 12 | 13 | Last but not least: 14 | - It's free! As in Speech and Beer. 15 | - OSS project lincensed under BSD license 16 | 17 | 18 | Goal: 19 | - Make it easy to work with images in various file formats in Java. 20 | 21 | Side goals for me: 22 | - Learn new stuff 23 | - Color theory, ICC profiles etc 24 | - Compressions and algorithms (even if I'm a little bored after writing 6-7 different RLE schemes) 25 | - Computer History 26 | - Meet people 27 | - Like Sigfried :-) 28 | 29 | 30 | History pt I: 31 | 32 | The name: 33 | - We were a couple of bored consultants that had worked together for years, 34 | not satisfied with the company we were working for, conspiring in a dark pub... 35 | - Maybe we should start our own company? 36 | - It never happened, but at least we found a name for our fictive company: 37 | 38 | TwelveMonkeys (yes, after the movie) 39 | 40 | 41 | History pt II: 42 | 43 | I was working at a CMS company, creating tools to suport the workflow of online publishing. 44 | - Escenic, large in the media industry (newspapers, TV). As we were serving the same content in various channels (web, mobile, TV, paper, etc); 45 | - We needed a way to achieve on-demand re-sizing of images 46 | - This is where the web/servlet part comes from 47 | 48 | - Image editor in our editing tool, Content Studio (Swing based) 49 | - Open any file 50 | - Copy/Paste, Drag/Drop 51 | 52 | - For weird reasons (historical) OS X clipboard data in Java is available in PICT format. However, there's no easy way to read PICT data in Java. PICT is an archaic format that uses op-codes to allow vector-graphics as well as regions of pixels. Just reading normal RGB pixel data is "quite easy". Result: One of the first plugins was the PICTImageReader 53 | 54 | 55 | - Working with an online digital workflow, pretty much every customer had PhotoShop 56 | - Being able to open Photoshop documents was a key feature. 57 | Result: The PSDImageReader. 58 | 59 | (PS: I once found that another Java-based CMS (that was later acquired by Adobe) uses my PSD plugin. So, ironically, Adobe uses my PSD plugin for Java. :-)) 60 | 61 | History pt III: 62 | 63 | Many years later I found myself working for an online bookstore. 64 | - Our problem was that 5-10% of the book cover images we had couldn't be properly opened and resized, even if they were in JPEG format. 65 | - Main problem: CMYK color model 66 | - Another common problem: Broken ICC profiles 67 | 68 | - The Sun/Oracle standard JPEGImageReader actually reads such files, but it can only be read using the less known readAsRaster method. 69 | - And you have to "manually" apply YCCK -> CMYK conversion, 70 | - before doing a CMYK -> RGB conversion (or apply ICC profile) for correct colors. 71 | - A lot of work. 72 | 73 | Result: The first version of the JPEGImageReader 74 | 75 | About the same time, I got a request from a friend 76 | - I can't read this JPEG, with a common AdobeRGB ICC profile..? 77 | - The Java2D ColorConvertOp that is used internally in the JPEGImageReader (default JRE and ours) when reading JPEGs with embedded color profiles, threw exceptions because of bad ICC profiles. Even if Java could otherwise read them fine... 78 | - Result: We now hot-fix a lot of the ICC profiles upon reading 79 | - Numerous other fixes are applied to bad/incorrect ICC profiles, like CorbisRGB profile etc 80 | - Bonus: We now have an internal cache of ICC color profiles and associated Java ColorSpace objects to allow reuse, and avoid excessive memory use when dealing with ICC profiles. 81 | 82 | - With "JPEG" there are so many different possible ways to encode. 83 | - In many cases, we just have to do our best, and show an image 84 | - Ignore some data, if multiple JPEG segments conflict 85 | - Log warnings, to alert the user 86 | - Don't crash (like the JRE version does) 87 | 88 | - Allow "inconsistent metadata" when reading 89 | - It's already in the file! 90 | - Let the client code/user decide what to keep 91 | - When writing, resolve in same way as reading, to make sure we only write good JPEGs 92 | 93 | 94 | Some notes/observations: 95 | - Surprisingly easy to write more plugins (especially ImageReaders) when you have already written one (especially if the one you did was TIFF :-)). 96 | - You basically needs the spec and some sample images 97 | - Most raster-based (not vector) image formats consists of pixels in RGB, gray or palette (indexed), layed out top/down (some bottom/up or even rotated) 98 | - PNM, PCX, TGA and SGI format plugins was written in as little as 3-4 evenings! 99 | - (+ a little more to fix some corner cases and meta data... ;-)) 100 | 101 | 102 | A note about deploying ImageIO plugins as part of your web app 103 | 104 | 105 | Because the ImageIO plugin registry (the IIORegistry) is "VM global", it doesn't by default work well with servlet contexts. This is especially evident if you load plugins from the WEB-INF/lib or classes folder. Unless you add ImageIO.scanForPlugins() somewhere in your code, the plugins might never be available at all. 106 | 107 | I addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context). If you restart your application, old classes will by default remain in memory forever (because the next time scanForPlugins is called, it's another ClassLoader that scans/loads classes, and thus they will be new instances in the registry). If a read is attempted using one of the remaining ("old") readers, weird exceptions (like NullPointerExceptions when accessing static final initialized fields) may occur. 108 | 109 | To work around both the discovery problem and the resource leak, it is recommended to use the IIOProviderContextListener that implements dynamic loading and unloading of ImageIO plugins for web applications. 110 | 111 | 112 | 113 | ... 114 | 115 | 116 | ImageIO service provider loader/unloader 117 | com.twelvemonkeys.servlet.image.IIOProviderContextListener 118 | 119 | 120 | ... 121 | 122 | 123 | 124 | 125 | Java 8 brings along one important change you should know about 126 | 127 | - The original, proprietary Kodak CMS (Color Managment System) that has been used in Java since the early days, has been replaced 128 | - Now uses the open source LittleCMS (de facto CMS for most OSS software for years) by default. 129 | - Widely used an maintained 130 | - Most users won't notice 131 | - Does affect ICC color profile handling (not 100% compatible) 132 | - Some profiles will crash/break on reading 133 | - Performance of ColorConvertOp drops :-( 134 | 135 | - For now (Java 8) it's possible to revert to the "old" KCMS using the switch 136 | - -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider 137 | - I recommend using this switch for now, if working with images 138 | 139 | 140 | 141 | 142 | Why chose TwelveMonkeys over other software? 143 | 144 | - Uses standard ImageIO API (no need to learn loads of new API) 145 | - Actively maintained 146 | - Large (and groving) user base 147 | - No native code 148 | - Open source with liberal license (BSD) 149 | 150 | Why not? 151 | 152 | - JAI ImageIO plugins still has better 153 | - Metadata and TIFF support 154 | - Lossless JPEG and JPEG-LS (using native library) 155 | 156 | - Commons Imaging (Sanselan) probably has better TIFF support (Fax compressions) 157 | 158 | 159 | 160 | Future/current work in progress: 161 | - TIFF: 162 | - metadata 163 | - write support 164 | 165 | - JPEG: 166 | - Lossless JPEG 167 | - Arithmetic coding 168 | - CMYK write support 169 | 170 | - Camera RAW plugins 171 | - CR2 (Cannon) 172 | - NEF (Nikon) 173 | - TIFF/EP and DNG (ISO/Adobe) 174 | (all TIFF/Exif derivates, so depends heavily on TIFF reading code + JPEG Lossless) 175 | --------------------------------------------------------------------------------