Posted By

lucaborace on 09/07/11


Tagged


Versions (?)

Image Resizer


 / Published in: Java
 

  1. package it.exprivia.hart.terzotempo.util.image;
  2.  
  3. import java.awt.AlphaComposite;
  4. import java.awt.Graphics2D;
  5. import java.awt.RenderingHints;
  6. import java.awt.image.BufferedImage;
  7. import java.awt.image.ConvolveOp;
  8. import java.awt.image.Kernel;
  9. import java.io.File;
  10. import java.io.FileOutputStream;
  11. import java.io.IOException;
  12. import java.io.InputStream;
  13. import java.util.Arrays;
  14.  
  15. import javax.imageio.ImageIO;
  16.  
  17. import sun.reflect.generics.reflectiveObjects.NotImplementedException;
  18.  
  19. import com.mortennobel.imagescaling.AdvancedResizeOp;
  20. import com.mortennobel.imagescaling.MultiStepRescaleOp;
  21. import com.sun.image.codec.jpeg.JPEGCodec;
  22. import com.sun.image.codec.jpeg.JPEGEncodeParam;
  23. import com.sun.image.codec.jpeg.JPEGImageEncoder;
  24.  
  25. public class Image {
  26.  
  27. ImageType sourceType = ImageType.UNKNOWN;
  28.  
  29. /**
  30.   * Load image from InputStream
  31.   * @param input
  32.   * @throws IOException
  33.   */
  34. Image(InputStream input, ImageType sourceType) throws IOException {
  35. img = ImageIO.read(input);
  36. input.close();
  37. this.sourceType = (sourceType == null ? ImageType.UNKNOWN : sourceType);
  38. }
  39.  
  40. /**
  41.   * Constructor for taking a BufferedImage
  42.   * @param img
  43.   */
  44. private Image(BufferedImage img, ImageType sourceType) {
  45. this.img = img;
  46. this.sourceType = (sourceType == null ? ImageType.UNKNOWN : sourceType);
  47. }
  48.  
  49. /**
  50.   * @return Source type of the image
  51.   */
  52. public ImageType getSourceType() {
  53. return sourceType;
  54. }
  55.  
  56. /**
  57.   * @return Width of the image in pixels
  58.   */
  59. public int getWidth() {
  60. return img.getWidth();
  61. }
  62.  
  63. /**
  64.   * @return Height of the image in pixels
  65.   */
  66. public int getHeight() {
  67. return img.getHeight();
  68. }
  69.  
  70. /**
  71.   * @return Aspect ratio of the image (width / height)
  72.   */
  73. public double getAspectRatio() {
  74. return (double)getWidth() / (double)getHeight();
  75. }
  76.  
  77. /**
  78.   * Generate a new Image object resized to a specific width, maintaining
  79.   * the same aspect ratio of the original
  80.   * @param width
  81.   * @return Image scaled to new width
  82.   */
  83. public Image getResizedToWidth(int width) {
  84. if (width > getWidth())
  85. throw new IllegalArgumentException("Width "+ width +" exceeds width of image, which is "+ getWidth());
  86. int nHeight = width * img.getHeight() / img.getWidth();
  87. MultiStepRescaleOp rescale = new MultiStepRescaleOp(width, nHeight);
  88. rescale.setUnsharpenMask(AdvancedResizeOp.UnsharpenMask.Soft);
  89. BufferedImage resizedImage = rescale.filter(img, null);
  90.  
  91. return new Image(resizedImage, sourceType);
  92. }
  93.  
  94.  
  95.  
  96. public Image scaleToSizes(int maxHeight,int maxWidth) {
  97. int nWidth = maxWidth;
  98. int nHeight = maxHeight;
  99. if(getHeight() > getWidth())
  100. nWidth = getWidth() * maxHeight/getHeight();
  101. else
  102. nHeight = getHeight() * maxWidth/getWidth();
  103.  
  104. MultiStepRescaleOp rescale = new MultiStepRescaleOp(nWidth, nHeight);
  105. rescale.setUnsharpenMask(AdvancedResizeOp.UnsharpenMask.Soft);
  106. BufferedImage resizedImage = rescale.filter(img, null);
  107.  
  108. return new Image(resizedImage, sourceType);
  109.  
  110. }
  111.  
  112. /**
  113.   * Generate a new Image object cropped to a new size
  114.   * @param x1 Starting x-axis position for crop area
  115.   * @param y1 Starting y-axis position for crop area
  116.   * @param x2 Ending x-axis position for crop area
  117.   * @param y2 Ending y-axis position for crop area
  118.   * @return Image cropped to new dimensions
  119.   */
  120. public Image crop(int x1, int y1, int x2, int y2) {
  121. if (x1 < 0 || x2 <= x1 || y1 < 0 || y2 <= y1 || x2 > getWidth() || y2 > getHeight())
  122. throw new IllegalArgumentException("invalid crop coordinates");
  123.  
  124. int type = img.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : img.getType();
  125. int nNewWidth = x2 - x1;
  126. int nNewHeight = y2 - y1;
  127. BufferedImage cropped = new BufferedImage(nNewWidth, nNewHeight, type);
  128. Graphics2D g = cropped.createGraphics();
  129.  
  130. g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
  131. g.setComposite(AlphaComposite.Src);
  132.  
  133. g.drawImage(img, 0, 0, nNewWidth, nNewHeight, x1, y1, x2, y2, null);
  134. g.dispose();
  135.  
  136. return new Image(cropped, sourceType);
  137. }
  138.  
  139. /**
  140.   * Useful function to crop and resize an image to a square.
  141.   * This is handy for thumbnail generation.
  142.   * @param width Width of the resulting square
  143.   * @param cropEdgesPct Specifies how much of an edge all around the square to crop,
  144.   * which creates a zoom-in effect on the center of the resulting square. This may
  145.   * be useful, given that when images are reduced to thumbnails, the detail of the
  146.   * focus of the image is reduced. Specifying a value such as 0.1 may help preserve
  147.   * this detail. You should experiment with it. The value must be between 0 and 0.5
  148.   * (representing 0% to 50%)
  149.   * @return Image cropped and resized to a square
  150.   */
  151. public Image getResizedToSquare(int width, double cropEdgesPct) {
  152. if (cropEdgesPct < 0 || cropEdgesPct > 0.5)
  153. throw new IllegalArgumentException("Crop edges pct must be between 0 and 0.5. "+ cropEdgesPct +" was supplied.");
  154. if (width > getWidth())
  155. throw new IllegalArgumentException("Width "+ width +" exceeds width of image, which is "+ getWidth());
  156. //crop to square first. determine the coordinates.
  157. int cropMargin = (int)Math.abs(Math.round(((img.getWidth() - img.getHeight()) / 2.0)));
  158. int x1 = 0;
  159. int y1 = 0;
  160. int x2 = getWidth();
  161. int y2 = getHeight();
  162. if (getWidth() > getHeight()) {
  163. x1 = cropMargin;
  164. x2 = x1 + y2;
  165. }
  166. else {
  167. y1 = cropMargin;
  168. y2 = y1 + x2;
  169. }
  170.  
  171. //should there be any edge cropping?
  172. if (cropEdgesPct != 0) {
  173. int cropEdgeAmt = (int)((double)(x2 - x1) * cropEdgesPct);
  174. x1 += cropEdgeAmt;
  175. x2 -= cropEdgeAmt;
  176. y1 += cropEdgeAmt;
  177. y2 -= cropEdgeAmt;
  178. }
  179.  
  180. // generate the image cropped to a square
  181. Image cropped = crop(x1, y1, x2, y2);
  182.  
  183. // now resize. we do crop first then resize to preserve detail
  184. Image resized = cropped.getResizedToWidth(width);
  185. cropped.dispose();
  186.  
  187. return resized;
  188. }
  189.  
  190. /**
  191.   * Soften the image to reduce pixelation. Helps JPGs look better after resizing.
  192.   * @param softenFactor Strength of softening. 0.08 is a good value
  193.   * @return New Image object post-softening, unless softenFactor == 0, in which
  194.   * case the same object is returned
  195.   * @throws NotImplementedException Not implemented in this class
  196.   */
  197. public Image soften(float softenFactor) {
  198. if (softenFactor == 0f)
  199. return this;
  200. else {
  201. float[] softenArray = {0, softenFactor, 0, softenFactor, 1-(softenFactor*4), softenFactor, 0, softenFactor, 0};
  202. Kernel kernel = new Kernel(3, 3, softenArray);
  203. ConvolveOp cOp = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
  204. return new Image(cOp.filter(img, null), sourceType);
  205. }
  206. }
  207.  
  208. /**
  209.   * Write image to a file. If the supplied File argument includes an extension,
  210.   * this method will attempt to write the image of that type, IF it is
  211.   * supported. Otherwise JPG is the default.
  212.   * If the supplied File argument does not include an extension, this
  213.   * method will attempt to write the image in the type of the original source
  214.   * image, IF it is supported. Otherwise JPG is the default.
  215.   * This method will overwrite a file that exists with the same name
  216.   * @see #getWriterFormatNames()
  217.   * @param file File to write image to
  218.   * @returns File object representing the image, which will have the proper
  219.   * extension appended if none was supplied (i.e. if the method chose the image type),
  220.   * or will have ".jpg" appended if the supplied extension is not supported
  221.   * @throws IOException
  222.   */
  223. public File writeToFile(File file) throws IOException {
  224. if (file == null)
  225. throw new IllegalArgumentException("File argument was null");
  226.  
  227. File writeto = null;
  228.  
  229. //extract extension
  230. String name = file.getName();
  231. String ext = null;
  232. int dot = name.lastIndexOf(".");
  233. if (dot != -1)
  234. ext = name.substring(dot + 1).toLowerCase();
  235.  
  236. //the presence of an extension in the file name tells us to
  237. //attempt to write the image of that type, if it is supported
  238. if (ext != null) {
  239. if (Arrays.asList(getWriterFormatNames()).contains(ext))
  240. writeto = file;
  241. else {
  242. //failing that, default to jpg
  243. ext = "jpg";
  244. writeto = new File(file.getPath() + ".jpg");
  245. }
  246. }
  247. else {
  248. //if no extension is supplied, try to use the image type of the source image
  249. if (Arrays.asList(getWriterFormatNames()).contains(getSourceType().toString().toLowerCase())) {
  250. ext = getSourceType().toString().toLowerCase();
  251. writeto = new File(file.getPath() +"."+ getSourceType().toString().toLowerCase());
  252. }
  253. else {
  254. //failing that, default to jpg
  255. ext = "jpg";
  256. writeto = new File(file.getPath() +".jpg");
  257. }
  258. }
  259. writeToFile(writeto, ext);
  260. return writeto;
  261. }
  262.  
  263. /**
  264.   * Write image to a file, specify image type
  265.   * This method will overwrite a file that exists with the same name
  266.   * @see #getWriterFormatNames()
  267.   * @param file File to write image to
  268.   * @param type jpg, gif, etc.
  269.   * @throws IOException
  270.   */
  271. public void writeToFile(File file, String type) throws IOException {
  272. if (file == null)
  273. throw new IllegalArgumentException("File argument was null");
  274. ImageIO.write(img, type, file);
  275. }
  276.  
  277. /**
  278.   * @return Array of supported image types for writing to file
  279.   */
  280. public String[] getWriterFormatNames() {
  281. return ImageIO.getWriterFormatNames();
  282. }
  283.  
  284.  
  285. /**
  286.   * Writes to JPG using Sun's JPEGCodec
  287.   * @param file File to write image to
  288.   * @param quality The image quality
  289.   * @throws IOException
  290.   */
  291. public void writeToJPG(File file, float quality) throws IOException {
  292.  
  293. // Encodes image as a JPEG data stream
  294. JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
  295.  
  296. JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(img);
  297.  
  298. param.setQuality(quality, true);
  299.  
  300. encoder.setJPEGEncodeParam(param);
  301. encoder.encode(img);
  302. }
  303.  
  304. /**
  305.   * Free up resources associated with this image
  306.   */
  307. public void dispose() {
  308. img.flush();
  309. }
  310.  
  311. /**
  312.   * @return Internal representation of the image
  313.   */
  314. // private BufferedImage getBufferedImage() {
  315. // return img;
  316. // }
  317.  
  318. }
  319.  
  320.  
  321. package it.exprivia.hart.terzotempo.util.image;
  322.  
  323. import java.util.HashMap;
  324. import java.util.Map;
  325.  
  326. public enum ImageType {
  327. JPG,
  328. GIF,
  329. PNG,
  330.  
  331. private static final Map<String, ImageType> extensionMap = new HashMap<String, ImageType>();
  332.  
  333. static {
  334. extensionMap.put("jpg", ImageType.JPG);
  335. extensionMap.put("jpeg", ImageType.JPG);
  336. extensionMap.put("gif", ImageType.GIF);
  337. extensionMap.put("png", ImageType.PNG);
  338. }
  339.  
  340. public static ImageType getType(String ext) {
  341. ext = ext.toLowerCase();
  342. if (extensionMap.containsKey(ext))
  343. return extensionMap.get(ext);
  344. else
  345. return UNKNOWN;
  346. }
  347. }
  348.  
  349.  
  350. package it.exprivia.hart.terzotempo.util.image;
  351.  
  352. import java.io.ByteArrayInputStream;
  353. import java.io.File;
  354. import java.io.FileInputStream;
  355. import java.io.IOException;
  356. import java.io.InputStream;
  357. import java.net.URL;
  358.  
  359. public class ImageLoader {
  360.  
  361. private ImageLoader() {}
  362.  
  363. /**
  364.   * Load image from URL
  365.   * @param url URL of image
  366.   * @throws IOException If there is a problem loading the image from the URL
  367.   */
  368. public static Image fromUrl(URL url) throws IOException {
  369. return new Image(url.openStream(), extensionToImageType(url.getPath()));
  370. }
  371.  
  372. /**
  373.   * Load image from URL string
  374.   * @param url URL of image
  375.   * @throws IOException If there is a problem loading the image from the URL
  376.   */
  377. public static Image fromUrl(String url) throws IOException {
  378. return fromUrl(new URL(url));
  379. }
  380.  
  381. /**
  382.   * Load image from file
  383.   * @param file
  384.   * @throws IOException If there is a problem loading the image from the file
  385.   */
  386. public static Image fromFile(File file) throws IOException {
  387. return new Image(new FileInputStream(file), extensionToImageType(file.getPath()));
  388. }
  389.  
  390. /**
  391.   * Load image from file string
  392.   * @param file
  393.   * @throws IOException If there is a problem loading the image from the file
  394.   */
  395. public static Image fromFile(String file) throws IOException {
  396. return fromFile(new File(file));
  397. }
  398.  
  399. /**
  400.   * Load image from byte array
  401.   * @param data image in the form of a byte array
  402.   * @throws IOException
  403.   */
  404. public static Image fromBytes(byte[] data) throws IOException {
  405. return fromBytes(data, ImageType.UNKNOWN);
  406. }
  407.  
  408. /**
  409.   * Load image from byte array
  410.   * @param data image in the form of a byte array
  411.   * @param sourceType hint that may be used when you eventually write the image
  412.   * @throws IOException
  413.   */
  414. public static Image fromBytes(byte[] data, ImageType sourceType) throws IOException {
  415. return new Image(new ByteArrayInputStream(data), sourceType);
  416. }
  417.  
  418. /**
  419.   * Load image from an input stream
  420.   * @param in image in the form of an input stream
  421.   * @throws IOException
  422.   */
  423. public static Image fromStream(InputStream in) throws IOException {
  424. return fromStream(in, ImageType.UNKNOWN);
  425. }
  426.  
  427. /**
  428.   * Load image from an input stream
  429.   * @param in image in the form of an input stream
  430.   * @param sourceType hint that may be used when you eventually write the image
  431.   * @throws IOException
  432.   */
  433. public static Image fromStream(InputStream in, ImageType sourceType) throws IOException {
  434. return new Image(in, sourceType);
  435. }
  436.  
  437. private static ImageType extensionToImageType(String path) {
  438. int idx = (path == null ? -1 : path.lastIndexOf("."));
  439. if (idx != -1)
  440. return ImageType.getType(path.substring(idx + 1));
  441. else
  442. return ImageType.UNKNOWN;
  443.  
  444. }
  445. }
  446.  
  447.  
  448.  
  449.  
  450. package it.exprivia.hart.terzotempo.util;
  451.  
  452. import it.exprivia.hart.terzotempo.util.image.Image;
  453. import it.exprivia.hart.terzotempo.util.image.ImageLoader;
  454.  
  455. import java.awt.AlphaComposite;
  456. import java.awt.Graphics2D;
  457. import java.awt.GraphicsConfiguration;
  458. import java.awt.RenderingHints;
  459. import java.awt.Transparency;
  460. import java.awt.image.BufferedImage;
  461. import java.awt.image.BufferedImageOp;
  462. import java.awt.image.ConvolveOp;
  463. import java.awt.image.Kernel;
  464. import java.io.File;
  465. import java.io.IOException;
  466. import java.util.HashMap;
  467. import java.util.Map;
  468.  
  469. import javax.imageio.ImageIO;
  470.  
  471. import org.apache.commons.logging.Log;
  472. import org.apache.commons.logging.LogFactory;
  473.  
  474. import sun.awt.image.BufferedImageGraphicsConfig;
  475.  
  476. public class ImageUtil {
  477.  
  478.  
  479. private static final int IMG_HEIGHT = 186;
  480. private static final int IMG_WIDTH = 186;
  481.  
  482.  
  483. public static void resize(File imageFile, File outputFile) throws IOException {
  484. Image img = ImageLoader.fromFile(imageFile);
  485.  
  486. Image resized = img.scaleToSizes(IMG_HEIGHT, IMG_WIDTH);
  487.  
  488. resized.soften(0.0f).writeToJPG(outputFile, 0.95f);
  489. }
  490.  
  491. }

Report this snippet  

You need to login to post a comment.