获取图片分辨率

在处理图片资源时,有时候需要获取图片的分辨率

虽然Java提供了ImageIO.read方法获取包含图片大小、尺寸宽高等数据的BufferedImage对象,但它需要把图片完全加载到内存中,如果图片比较大,会占用较大的内存,其次它也不能读取PSD等源文件格式

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class ImageTest {
    public static void main(String[] args) throws IOException {
        String filePath = args[0];
        BufferedImage image = ImageIO.read(new File(filePath));
        int width = image.getWidth();
        int height = image.getHeight();
        System.out.println("width:" + width + " height:" + height);
    }
}

当然也可以使用im4java等第三方包读取,调用时将工具地址传进来,例如:

import org.im4java.core.Info;
import org.im4java.core.InfoException;

public final class Im4JavaUtil {
    private Im4JavaUtil() {}

    /**
     * 获取图像尺寸
     *
     * @param imagePath 图片路径
     * @return 图像的尺寸
     */
    public static int[] getImageInfo(String imagePath) throws InfoException {
        int[] size = new int[2];
        Info imageInfo = new Info(imagePath, true);
        size[0] = imageInfo.getImageWidth();
        size[1] = imageInfo.getImageHeight();
        return size;
    }
}

这种方式也有缺点,它会通过Process拉起子进程进行处理,不太方便对CPU等指标进行监控,如果同时处理的子进程过多,有可能会导致系统崩溃

因为这里我们只想要图片的分辨率,因此最好是通过解析不同图片格式协议的方式,只需要读取文件的前一些字节就能够获取到分辨率等数据,并且占用内存非常小

例如PSD文件头格式定义如下:

  • 魔数:4字节,固定为8BPS
  • 版本: 2字节,例如:1
  • 保留字节: 6字节,固定为0
  • 图像的通道数量 : 2字节
  • 图像的高度: 4字节
  • 图像的宽度:4字节
  • 每通道的位数: 2字节
  • 颜色模式 : 2字节

这里使用组合器的方式实现,这样扩展性更好,可以根据不同的业务需求添加自定义的解析器

1)首先创建一个解析器接口,图片的格式一般可以通过前4个字节确认

public interface ImageResolutionResolver {
    /**
     * 判断是否支持处理此类型
     *
     * @param c1 第一个字节
     * @param c2 第二个字节
     * @param c3 第三个字节
     * @param c4 第四个字节
     * @return 是否支持处理
     */
    boolean support(int c1, int c2, int c3, int c4);

    /**
     * 获取图片分辨率
     *
     * @param fis       文件字节流
     * @param c1        第一个字节
     * @param c2        第二个字节
     * @param c3        第三个字节
     * @param c4        第四个字节
     * @return 图片分辨率
     */
    int[] resolve(FileInputStream fis, int c1, int c2, int c3, int c4) throws IOException;

    /**
     * 读取时需要区分大小端
     */
    private static int readInt(InputStream is, int length, boolean bigEndian) throws IOException {
        int ret = 0;
        int sv = bigEndian ? ((length - 1) * 8) : 0;
        int cnt = bigEndian ? -8 : 8;
        for (int i = 0; i < length; i++) {
            // 读取一个字节,如果是大端,则左移,这里的或等相当于相加运算
            ret |= is.read() << sv;
            sv += cnt;
        }
        return ret;
    }
}

注意:

  1. 不同图片格式文件的字节序是不一样的,具体参考协议官方文档
  2. 因为大端存储时数据的高位存储在内存的低地址处,因此在解析时需要移位

2)创建各种不同图片格式的解析器,这些解析器都实现ImageResolutionResolver接口

Bmp格式解析器:

public class BmpResolutionResolver implements ImageResolutionResolver {
    @Override
    public boolean support(int c1, int c2, int c3, int c4) {
        if (c1 == 66 && c2 == 77) {
            return true;
        }
        return false;
    }

    @Override
    public int[] resolve(FileInputStream fis, int c1, int c2, int c3, int c4) throws IOException {
        fis.skip(14);
        int width = readInt(fis, 2, false);
        fis.skip(2);
        int height = readInt(fis, 2, false);
        return new int[]{width, height};
    }
}

Gif格式解析器:

public class GifResolutionResolver implements ImageResolutionResolver {
    @Override
    public boolean support(int c1, int c2, int c3, int c4) {
        if (c1 == 'G' && c2 == 'I' && c3 == 'F') {
            return true;
        }
        return false;
    }

    @Override
    public int[] resolve(FileInputStream fis, int c1, int c2, int c3, int c4) throws IOException {
        fis.skip(2);
        int width = readInt(fis, 2, false);
        int height = readInt(fis, 2, false);
        return new int[]{width, height};
    }
}

JPG/JPEG格式解析器:

public class JpgResolutionResolver implements ImageResolutionResolver {
    @Override
    public boolean support(int c1, int c2, int c3, int c4) {
        if (c1 == 0xFF && c2 == 0xD8) {
            return true;
        }
        return false;
    }

    @Override
    public int[] resolve(FileInputStream fis, int c1, int c2, int c3, int c4) throws IOException {
        while (c3 == 255) {
            int len = readInt(fis, 2, true);
            if (c4 == 192 || c4 == 193 || c4 == 194) {
                fis.skip(1);
                int height = readInt(fis, 2, true);
                int width = readInt(fis, 2, true);
                return new int[]{width, height};
            }
            fis.skip(len - 2);
            c3 = fis.read();
            c4 = fis.read();
        }
        return null;
    }
}

Png格式解析器:

public class PngResolutionResolver implements ImageResolutionResolver {
    @Override
    public boolean support(int c1, int c2, int c3, int c4) {
        if (c1 == 137 && c2 == 80 && c3 == 78) {
            return true;
        }
        return false;
    }

    @Override
    public int[] resolve(FileInputStream fis, int c1, int c2, int c3, int c4) throws IOException {
        fis.skip(14);
        int width = readInt(fis, 2, true);
        fis.skip(2);
        int height = readInt(fis, 2, true);
        return new int[]{width, height};
    }
}

PSD格式解析器:

public class PsdResolutionResolver implements ImageResolutionResolver {
    @Override
    public boolean support(int c1, int c2, int c3, int c4) {
        if (c1 == '8' && c2 == 'B' && c3 == 'P' && c4 == 'S') {
            return true;
        }
        return false;
    }

    @Override
    public int[] resolve(FileInputStream fis, int c1, int c2, int c3, int c4) throws IOException {
        fis.skip(10);
        int height = readInt(fis, 4, true);
        int width = readInt(fis, 4, true);
        return new int[] {width, height};
    }
}

3)创建解析器组合器类,构造组合器时将需要的解析器传入

public class ImageResolutionResolverComposite {
    private List<ImageResolutionResolver> resolvers = new LinkedList<>();

    private boolean bigEndian = true;

    public ImageResolutionResolverComposite() {
        resolvers.add(new BmpResolutionResolver());
        resolvers.add(new GifResolutionResolver());
        resolvers.add(new JpgResolutionResolver());
        resolvers.add(new PngResolutionResolver());
        resolvers.add(new PsdResolutionResolver());
    }

    public int[] processImageSize(String filePath) throws IOException {
        try (FileInputStream fis = new FileInputStream(filePath)) {
            int c1 = fis.read();
            int c2 = fis.read();
            int c3 = fis.read();
            int c4 = fis.read();
            return resolve(fis, c1, c2, c3, c4);
        }
    }

    private int[] resolve(FileInputStream fis, int c1, int c2, int c3, int c4) throws IOException {
        for (ImageResolutionResolver resolver : resolvers) {
            if (resolver.support(c1, c2, c3, c4)) {
                return resolver.resolve(fis, c1, c2, c3, c4);
            }
        }
        return null;
    }
}

4)测试代码

public class ImageTest {
    public static void main(String[] args) throws IOException {
        String filePath = args[0];
        ImageResolutionResolverComposite composite = new ImageResolutionResolverComposite();
        int[] resolution = composite.processImageSize(filePath);
        System.out.println("width:" + resolution[0] + " height:" + resolution[1]);
    }
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lm_ylj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值