Pau*_*lor 7 java bufferedimage java-2d java-8
(使用java 8)鉴于图像用户需要能够以像素为单位指定最小/最大图像大小,并且还需要以kbs为单位指定已保存图像的最大大小,图像将保存为jpg.
所以我通过调整缓冲图像的大小来完成第一个工作:
public static BufferedImage resizeUsingImageIO(Image srcImage, int size)
{
int w = srcImage.getWidth(null);
int h = srcImage.getHeight(null);
// Determine the scaling required to get desired result.
float scaleW = (float) size / (float) w;
float scaleH = (float) size / (float) h;
MainWindow.logger.finest("Image Resizing to size:" + size + " w:" + w + ":h:" + h + ":scaleW:" + scaleW + ":scaleH" + scaleH);
//Create an image buffer in which to paint on, create as an opaque Rgb type image, it doesn't matter what type
//the original image is we want to convert to the best type for displaying on screen regardless
BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
// Set the scale.
AffineTransform tx = new AffineTransform();
tx.scale(scaleW, scaleH);
// Paint image.
Graphics2D g2d = bi.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, size, size);
g2d.setComposite(AlphaComposite.SrcOver);
g2d.drawImage(srcImage, tx, null);
g2d.dispose();
return bi;
}
Run Code Online (Sandbox Code Playgroud)
最终将图像输出为jpg,如下所示
public static byte[] convertToByteArray(BufferedImage bi) throws Exception
{
final ByteArrayOutputStream output = new ByteArrayOutputStream();
//Convert JPEG and then a byte array
if (ImageIO.write(bi, FILE_SUFFIX_JPG, new DataOutputStream(output)))
{
final byte[] imageData = output.toByteArray();
return imageData;
}
}
Run Code Online (Sandbox Code Playgroud)
但有没有办法指定最大图像大小,并使其根据需要执行更多压缩以获得该大小.
并且我应该根据所需的总尺寸在第一阶段设置宽度和高度的限制,即如果总尺寸太小,如果压缩到太小的尺寸则不可能获得良好的图像
我不知道这是一种"简单"或"优雅"的方式.
但是,1.5年前,我编写了这段代码片段:它是一个小实用程序,允许选择图像分辨率,压缩质量和生成的JPG文件大小,并显示生成的图像的预览.
图像质量的滑块和JPG文件大小的微调器是"链接的":更改质量时,将更新生成的文件大小.更改文件大小时,将调整质量,使得生成的图像不大于给定的文件大小(如果可能).
质量调整是使用某种"二分搜索"(参见computeQuality方法)完成的,因为根据压缩预测文件大小很难(甚至不可能).当然,这意味着一些计算成本,但我认为没有那么多的替代品.(您可以定义另一个停止标准.目前,它很难找到给定文件大小限制的"完美"质量).无论如何,这个实用程序中的一个或另一个方法可能对您有所帮助.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ImageLimiterTest
{
public static void main(String[] args) throws IOException
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
try
{
createAndShowGUI();
}
catch (IOException e)
{
e.printStackTrace();
}
}
});
}
private static void createAndShowGUI() throws IOException
{
JFrame f = new JFrame("ImageLimiter");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ImageLimiterPanel imageLimiterPanel = new ImageLimiterPanel(new ImageLimiter());
BufferedImage inputImage = ImageIO.read(new File("lena512color.png"));
imageLimiterPanel.setInputImage(inputImage);
f.getContentPane().add(imageLimiterPanel);
f.setSize(800,600);
f.setVisible(true);
}
}
class ImageLimiter
{
private BufferedImage inputImage;
private BufferedImage scaledImage;
private BufferedImage outputImage;
private int maxResolution;
private float quality;
private int fileSizeBytes;
public void setInputImage(BufferedImage inputImage)
{
this.inputImage = inputImage;
this.maxResolution = Math.max(inputImage.getWidth(), inputImage.getHeight());
this.quality = 1.0f;
this.scaledImage = computeScaledImage(inputImage, maxResolution);
updateOutputImage();
}
public BufferedImage getOutputImage()
{
return outputImage;
}
public int getFileSizeBytes()
{
return fileSizeBytes;
}
public void setMaxResolution(int maxResolution)
{
this.maxResolution = maxResolution;
this.scaledImage = computeScaledImage(inputImage, maxResolution);
updateOutputImage();
}
public void setQuality(float quality)
{
this.quality = quality;
updateOutputImage();
}
public float getQuality()
{
return quality;
}
public void setMaxFileSize(int maxFileSizeBytes)
{
this.quality = computeQuality(scaledImage, maxFileSizeBytes);
updateOutputImage();
}
private void updateOutputImage()
{
try
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeJPG(scaledImage, baos, quality);
baos.close();
byte data[] = baos.toByteArray();
fileSizeBytes = data.length;
ByteArrayInputStream bais = new ByteArrayInputStream(data);
outputImage = ImageIO.read(bais);
bais.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
static float computeQuality(BufferedImage image, int sizeLimitBytes)
{
int minSizeBytes = computeSizeBytes(image, 0.0f);
if (sizeLimitBytes < minSizeBytes)
{
return 0.0f;
}
int maxSizeBytes = computeSizeBytes(image, 1.0f);
if (sizeLimitBytes > maxSizeBytes)
{
return 1.0f;
}
float intervalSize = 0.5f;
float quality = 0.5f;
float lastSmaller = 0;
while (true)
{
int sizeBytes = computeSizeBytes(image, quality);
if (sizeBytes >= sizeLimitBytes)
{
//System.out.println("For "+quality+" have size "+sizeBytes+", decrease quality by "+intervalSize);
quality -= intervalSize;
intervalSize /= 2;
}
else if (sizeBytes < sizeLimitBytes)
{
//System.out.println("For "+quality+" have size "+sizeBytes+", increase quality by "+intervalSize);
lastSmaller = quality;
quality += intervalSize;
intervalSize /= 2;
}
if (intervalSize < 0.01f)
{
break;
}
}
return lastSmaller;
}
private static int computeSizeBytes(BufferedImage image, float quality)
{
quality = Math.min(1, Math.max(0, quality));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try
{
writeJPG(image, baos, quality);
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
try
{
baos.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
byte data[] = baos.toByteArray();
return data.length;
}
private static BufferedImage computeScaledImage(BufferedImage input, int limit)
{
int w = input.getWidth();
int h = input.getHeight();
float aspect = (float)w / h;
if (aspect > 1)
{
w = limit;
h = (int)(w / aspect);
}
else
{
h = limit;
w = (int)(h * aspect);
}
BufferedImage output = new BufferedImage(
w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = output.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(input, 0, 0, w, h, null);
g.dispose();
return output;
}
/**
* Write the given RenderedImage as a JPEG to the given outputStream,
* using the given quality. The quality must be a value between
* 0 (lowest quality, maximum compression) and 1 (highest
* quality, minimum compression). The caller is responsible for
* closing the given stream.
*
* @param renderedImage The image to write
* @param outputStream The stream to write to
* @param quality The quality, between 0 and 1
* @throws IOException If an IO error occurs
*/
public static void writeJPG(RenderedImage renderedImage,
OutputStream outputStream, float quality) throws IOException
{
Iterator<ImageWriter> imageWriters =
ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter imageWriter = imageWriters.next();
ImageOutputStream imageOutputStream =
ImageIO.createImageOutputStream(outputStream);
imageWriter.setOutput(imageOutputStream);
ImageWriteParam param = imageWriter.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality);
IIOImage iioImage = new IIOImage(renderedImage, null, null);
imageWriter.write(null, iioImage, param);
}
}
class ImageLimiterPanel extends JPanel
{
private ImageLimiter imageLimiter;
private ImageIcon inputImageIcon;
private ImageIcon outputImageIcon;
private JScrollPane inputScrollPane;
private JScrollPane outputScrollPane;
private JSlider qualitySlider;
private JLabel qualityLabel;
private JSlider resolutionLimitSlider;
private JLabel resolutionLimitLabel;
private JSpinner sizeLimitSpinner;
private JLabel sizeLimitLabel;
private boolean updating = false;
public ImageLimiterPanel(ImageLimiter imageLimiter)
{
this.imageLimiter = imageLimiter;
setLayout(new BorderLayout());
final JSplitPane splitPane = new JSplitPane();
inputImageIcon = new ImageIcon();
JLabel inputImageLabel = new JLabel(inputImageIcon);
inputScrollPane = new JScrollPane(inputImageLabel);
inputScrollPane.setBorder(BorderFactory.createTitledBorder("Input"));
splitPane.setLeftComponent(inputScrollPane);
outputImageIcon = new ImageIcon();
JLabel outputImageLabel = new JLabel(outputImageIcon);
outputScrollPane = new JScrollPane(outputImageLabel);
outputScrollPane.setBorder(BorderFactory.createTitledBorder("Output"));
splitPane.setRightComponent(outputScrollPane);
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
splitPane.setDividerLocation(0.5);
}
});
add(splitPane, BorderLayout.CENTER);
JPanel controlPanel = new JPanel(new GridLayout(0,1));
JPanel resolutionLimitPanel = createResolutionLimitPanel();
controlPanel.add(resolutionLimitPanel);
JPanel qualityPanel = createQualityPanel();
controlPanel.add(qualityPanel);
JPanel sizePanel = createSizeLimitPanel();
controlPanel.add(sizePanel);
add(controlPanel, BorderLayout.NORTH);
}
public void setInputImage(BufferedImage inputImage)
{
imageLimiter.setInputImage(inputImage);
inputImageIcon.setImage(inputImage);
int max = Math.max(inputImage.getWidth(), inputImage.getHeight());
resolutionLimitSlider.setMaximum(max);
resolutionLimitSlider.setValue(max);
}
private JPanel createResolutionLimitPanel()
{
JPanel resolutionLimitPanel = new JPanel(new BorderLayout());
resolutionLimitLabel = new JLabel("Resolution: ");
resolutionLimitLabel.setPreferredSize(new Dimension(300, 10));
resolutionLimitPanel.add(resolutionLimitLabel, BorderLayout.WEST);
resolutionLimitSlider = new JSlider(0,100,80);
resolutionLimitSlider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent arg0)
{
if (!updating)
{
updating = true;
int maxResolution = resolutionLimitSlider.getValue();
imageLimiter.setMaxResolution(maxResolution);
updateOutputImage(imageLimiter.getOutputImage());
sizeLimitLabel.setText("Size: "+imageLimiter.getFileSizeBytes());
sizeLimitSpinner.setValue(imageLimiter.getFileSizeBytes());
updating = false;
}
}
});
resolutionLimitPanel.add(resolutionLimitSlider, BorderLayout.CENTER);
return resolutionLimitPanel;
}
private JPanel createQualityPanel()
{
JPanel qualityPanel = new JPanel(new BorderLayout());
qualityLabel = new JLabel("Quality: ");
qualityLabel.setPreferredSize(new Dimension(300, 10));
qualityPanel.add(qualityLabel, BorderLayout.WEST);
qualitySlider = new JSlider(0,100,80);
qualitySlider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent arg0)
{
if (!updating)
{
updating = true;
float quality = qualitySlider.getValue()/100.0f;
imageLimiter.setQuality(quality);
updateOutputImage(imageLimiter.getOutputImage());
qualityLabel.setText("Quality: "+String.format("%.2f", quality));
sizeLimitLabel.setText("Size: "+imageLimiter.getFileSizeBytes());
sizeLimitSpinner.setValue(imageLimiter.getFileSizeBytes());
updating = false;
}
}
});
qualityPanel.add(qualitySlider, BorderLayout.CENTER);
return qualityPanel;
}
private JPanel createSizeLimitPanel()
{
JPanel sizeLimitPanel = new JPanel(new BorderLayout());
sizeLimitLabel = new JLabel("Size: ");
sizeLimitLabel.setPreferredSize(new Dimension(300, 10));
sizeLimitPanel.add(sizeLimitLabel, BorderLayout.WEST);
sizeLimitSpinner = new JSpinner(new SpinnerNumberModel(10000, 0, 1000000000, 1000));
sizeLimitSpinner.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent arg0)
{
if (!updating)
{
updating = true;
int sizeLimit = (Integer)sizeLimitSpinner.getValue();
imageLimiter.setMaxFileSize(sizeLimit);
updateOutputImage(imageLimiter.getOutputImage());
qualityLabel.setText("Quality: "+String.format("%.2f", imageLimiter.getQuality()));
qualitySlider.setValue((int)(imageLimiter.getQuality()*100));
sizeLimitLabel.setText("Size: "+imageLimiter.getFileSizeBytes());
sizeLimitSpinner.setValue(imageLimiter.getFileSizeBytes());
updating = false;
}
}
});
sizeLimitPanel.add(sizeLimitSpinner, BorderLayout.CENTER);
return sizeLimitPanel;
}
private void updateOutputImage(BufferedImage outputImage)
{
outputImageIcon.setImage(outputImage);
outputScrollPane.invalidate();
revalidate();
outputScrollPane.repaint();
}
}
Run Code Online (Sandbox Code Playgroud)