JAVA

[Java/자바] 이미지 리사이즈(ver. java)

밍글링글링 2018. 5. 9. 16:56
728x90

이미지 리사이즈.

우선, sanselan 사용할 것이다.

왜냐하면, 그냥 ImageIO만 사용하게되면 간혹 포토샵을 거친 이미지들을 읽지 못하는 경우가 있기 때문에,

자바오픈소스인 sanselan을 이용하여 이미지정보(크기, 색 공간, ICC프로파일 등) 및 메타 데이터의 빠른 구문 분석을 비롯하여

다양한 이미지 형식을 읽고 쓸 수 있어 자유로워서 사용하는 것을 권장한다.

 

Maven을 사용한다면 아래와 같이.

pom.xml

<dependency>
    <groupId>org.apache.sanselan</groupId>
    <artifactId>sanselan</artifactId>
    <version>0.97-incubator</version>
</dependency>

Maven을 사용하지 않고, jar파일로 사용할 예정이면

sanselan-0.97-incubator.jar
다운로드

▲ 위의 파일을 다운로드 받아 사용하자.

JpegReader.java

package jungle.common.util;

import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;

import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.Sanselan;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.common.byteSources.ByteSourceFile;
import org.apache.sanselan.formats.jpeg.JpegImageParser;
import org.apache.sanselan.formats.jpeg.segments.UnknownSegment;

public class JpegReader {
     
    public static final int COLOR_TYPE_RGB = 1;
    public static final int COLOR_TYPE_CMYK = 2;
    public static final int COLOR_TYPE_YCCK = 3;

    private int colorType = COLOR_TYPE_RGB;
    private boolean hasAdobeMarker = false;

    public BufferedImage readImage(File file) throws IOException, ImageReadException {
        colorType = COLOR_TYPE_RGB;
        hasAdobeMarker = false;

        ImageInputStream stream = ImageIO.createImageInputStream(file);
        Iterator<ImageReader> iter = ImageIO.getImageReaders(stream);
        while (iter.hasNext()) {
            ImageReader reader = iter.next();
            reader.setInput(stream);

            BufferedImage image;
            ICC_Profile profile = null;
            try {
                image = reader.read(0);
            } catch (IIOException e) {
                colorType = COLOR_TYPE_CMYK;
                checkAdobeMarker(file);
                profile = Sanselan.getICCProfile(file);
                WritableRaster raster = (WritableRaster) reader.readRaster(0, null);
                if (colorType == COLOR_TYPE_YCCK){
                    convertYcckToCmyk(raster);
                }
                 
                if (hasAdobeMarker){
                    convertInvertedColors(raster);
                }
                image = convertCmykToRgb(raster, profile);
            } finally {
                stream.close(); //원본에는 close해주는 부분이 없어서 추가해 줬음, 위치상으로 애매한데 리턴 전이라 그냥 닫아줌
            }

            return image;
        }

        return null;
    }

    public void checkAdobeMarker(File file) throws IOException, ImageReadException {
        JpegImageParser parser = new JpegImageParser();
        ByteSource byteSource = new ByteSourceFile(file);
        @SuppressWarnings("rawtypes")
        ArrayList segments = parser.readSegments(byteSource, new int[] { 0xffee }, true);
        if (segments != null && segments.size() >= 1) {
            UnknownSegment app14Segment = (UnknownSegment) segments.get(0);
            byte[] data = app14Segment.bytes;
            if (data.length >= 12 && data[0] == 'A' && data[1] == 'd' && data[2] == 'o' && data[3] == 'b' && data[4] == 'e')
            {
                hasAdobeMarker = true;
                int transform = app14Segment.bytes[11] & 0xff;
                if (transform == 2)
                    colorType = COLOR_TYPE_YCCK;
            }
        }
    }

    public static void convertYcckToCmyk(WritableRaster raster) {
        int height = raster.getHeight();
        int width = raster.getWidth();
        int stride = width * 4;
        int[] pixelRow = new int[stride];
        for (int h = 0; h < height; h++) {
            raster.getPixels(0, h, width, 1, pixelRow);

            for (int x = 0; x < stride; x += 4) {
                int y = pixelRow[x];
                int cb = pixelRow[x + 1];
                int cr = pixelRow[x + 2];

                int c = (int) (y + 1.402 * cr - 178.956);
                int m = (int) (y - 0.34414 * cb - 0.71414 * cr + 135.95984);
                y = (int) (y + 1.772 * cb - 226.316);

                if (c < 0) c = 0; else if (c > 255) c = 255;
                if (m < 0) m = 0; else if (m > 255) m = 255;
                if (y < 0) y = 0; else if (y > 255) y = 255;

                pixelRow[x] = 255 - c;
                pixelRow[x + 1] = 255 - m;
                pixelRow[x + 2] = 255 - y;
            }

            raster.setPixels(0, h, width, 1, pixelRow);
        }
    }

    public static void convertInvertedColors(WritableRaster raster) {
        int height = raster.getHeight();
        int width = raster.getWidth();
        int stride = width * 4;
        int[] pixelRow = new int[stride];
        for (int h = 0; h < height; h++) {
            raster.getPixels(0, h, width, 1, pixelRow);
            for (int x = 0; x < stride; x++)
                pixelRow[x] = 255 - pixelRow[x];
            raster.setPixels(0, h, width, 1, pixelRow);
        }
    }

    public static BufferedImage convertCmykToRgb(Raster cmykRaster, ICC_Profile cmykProfile) throws IOException {

        if (cmykProfile == null){
            cmykProfile = ICC_Profile.getInstance(JpegReader.class.getResourceAsStream("ISOcoated_v2_300_eci.icc"));
        }

        ICC_ColorSpace cmykCS = new ICC_ColorSpace(cmykProfile);
        BufferedImage rgbImage = new BufferedImage(cmykRaster.getWidth(), cmykRaster.getHeight(), BufferedImage.TYPE_INT_RGB);
        WritableRaster rgbRaster = rgbImage.getRaster();
        ColorSpace rgbCS = rgbImage.getColorModel().getColorSpace();
        ColorConvertOp cmykToRgb = new ColorConvertOp(cmykCS, rgbCS, null);
        cmykToRgb.filter(cmykRaster, rgbRaster);
        return rgbImage;
    }

    static void intToBigEndian(int value, byte[] array, int index) {
        array[index]   = (byte) (value >> 24);
        array[index+1] = (byte) (value >> 16);
        array[index+2] = (byte) (value >>  8);
        array[index+3] = (byte) (value);
    }
     
}

JpegReader.java를 쓴 경로에 

ISOcoated_v2_300_eci.icc
다운로드

파일을 추가해준다.

JpegReader를 사용할 때 필요하다.

public class FileUtil {

//****로컬용    
//    public static final String filePath = "C:\\TEMP\\";
//****
    
//****서버용    /filesvr/logo/
    public static final String filePath = "/data/fileserver/";
    public static final String thumbnailPath = "/data/fileserver/thumbnail/";

public void createdThumbnail(String newFileName, String folder, String ext){
        String filename = filePath + folder + newFileName;  
        String savename = thumbnailPath + newFileName;    // 새 이미지 파일명
        int newWidth = 600;                                  // 변경 할 넓이
        int newHeight = 700;                                 // 변경 할 높이
        String mainPosition = "W"; 
        
        int imageWidth;
        int imageHeight;
        double ratio;
        int w;
        int h;
        
        JpegReader jpegReader = new JpegReader(); //위에서 만든 Class
        Image bufferedImage = null;
         
        try {
            bufferedImage = jpegReader.readImage(new File(filename));
            
            // 원본 이미지 사이즈 가져오기
            imageWidth = bufferedImage.getWidth(null);
            imageHeight = bufferedImage.getHeight(null);

            if(mainPosition.equals("W")){    // 넓이기준

                ratio = (double)newWidth/(double)imageWidth;
                w = (int)(imageWidth * ratio);
                h = (int)(imageHeight * ratio);

            }else if(mainPosition.equals("H")){ // 높이기준

                ratio = (double)newHeight/(double)imageHeight;
                w = (int)(imageWidth * ratio);
                h = (int)(imageHeight * ratio);

            }else{ //설정값 (비율무시)

                w = newWidth;
                h = newHeight;
            }
            
            Image resizeImage = bufferedImage.getScaledInstance(w, h, Image.SCALE_SMOOTH);
            
            // 새 이미지  저장하기
            BufferedImage newImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
            Graphics g = newImage.getGraphics();
            g.drawImage(resizeImage, 0, 0, null);
            g.dispose();
            ImageIO.write(newImage, ext, new File(savename));//동일한 명으로 이미지를 변경한다. 복사본을 남기려면 outputfile의 파일명을 변경
             
        } catch (ImageReadException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

 

사용할 JAVA파일에서 아래와 같이 사용하면 저용량 이미지로 복사된다.

fileUtil.createdThumbnail(newFileName, folder, ext);
728x90