压缩图片至额定大小

本文介绍了一种基于Java的图片压缩方法,通过调整图片尺寸、质量和使用特定的图像处理技术来实现图片的有效压缩,确保压缩后的图片既保持了良好的视觉效果又减少了文件大小。

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import javax.imageio.ImageIO;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;


/**
* 根据图片路径压缩图片至额定大小
* @param picUrl 需要压缩的图片路径
* @param scale 压缩比例 不能大于1,默认0.5
* @param quality 压缩品质介于0.1~1.0之间,默认0.75
* @param imgSize 图片大小压缩上限
* @param img 二次压缩图片对象
* @return
* @throws IOException
*/
public static byte[] imageCompress(String picUrl, float scale, float quality, long imgSize, Image img) throws IOException{
Image image = null;
if(img == null){//第一次是读取image
//根据图片路径获取图片转为字节输入流
URL url = new URL(picUrl);
URLConnection uc = url.openConnection();
InputStream in = uc.getInputStream();
//字节输入流转为要输出流
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
byte[] buff = new byte[100];
int rc = 0;
while ((rc = in.read(buff)) > 0) {
swapStream.write(buff, 0, rc);
}
byte[] imagSize = swapStream.toByteArray();

// System.out.println("图片实际大小:"+imagSize.length);
//如果图片本身大小已在限额内则直接返回图片字节输出流
if(imagSize.length <= imgSize)
return imagSize;
else
image = javax.imageio.ImageIO.read(url);
}
else{//第二次开始不读图片,直接把第一次的图片压缩
image = img;
}
//图片本身大小已超过限额,则根据比率从尺寸上降低
int imageWidth = image.getWidth(null);
int imageHeight = image.getHeight(null);
imageWidth = (int)(scale*imageWidth);
imageHeight = (int)(scale*imageHeight);

image = image.getScaledInstance(imageWidth, imageHeight, Image.SCALE_AREA_AVERAGING);
// Make a BufferedImage from the Image.
BufferedImage mBufferedImage = new BufferedImage(imageWidth, imageHeight,BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = mBufferedImage.createGraphics();

g2.drawImage(image, 0, 0,imageWidth, imageHeight, Color.white,null);
g2.dispose();

float[] kernelData2 = {
-0.125f, -0.125f, -0.125f,
-0.125f,2, -0.125f,
-0.125f,-0.125f, -0.125f};
Kernel kernel = new Kernel(3, 3, kernelData2);
ConvolveOp cOp = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
mBufferedImage = cOp.filter(mBufferedImage, null);

ByteArrayOutputStream out = new ByteArrayOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(mBufferedImage);
// System.out.println(quality);
//降低图像品质(不是降低DPI)
param.setQuality(quality, true);//
encoder.setJPEGEncodeParam(param);
encoder.encode(mBufferedImage);
byte[] comImage = out.toByteArray();
// System.out.println(comImage.length);
if(comImage.length > imgSize){//如果图像压缩后大小还是超过限额则继续降低压缩比率降低品质
scale = scale - 0.10f;
quality = quality - 0.10f;
imageCompress(picUrl, scale, quality, imgSize, image);
}
return comImage;
}

/**
* 压缩图片到指定品质和比例
* @param picUrl 需要压缩的图片路径
* @param fileName 要压缩的图片名称
* @param toFileName 压缩后的图片名称
* @param scale 压缩比例 不能大于1,默认0.5
* @param quality 压缩品质介于0.1~1.0之间
*/
@SuppressWarnings("unused")
private static void imageCompress(String picUrl, String fileName,String toFileName,
float scale, float quality){
try {
long start = System.currentTimeMillis();
Image image = javax.imageio.ImageIO.read(new File(picUrl + fileName));
int imageWidth = image.getWidth(null);
int imageHeight = image.getHeight(null);
imageWidth = (int)(scale*imageWidth);
imageHeight = (int)(scale*imageHeight);

image = image.getScaledInstance(imageWidth, imageHeight, Image.SCALE_AREA_AVERAGING);
// Make a BufferedImage from the Image.
BufferedImage mBufferedImage = new BufferedImage(imageWidth, imageHeight,BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = mBufferedImage.createGraphics();

g2.drawImage(image, 0, 0,imageWidth, imageHeight, Color.white,null);
g2.dispose();

float[] kernelData2 = {
-0.125f, -0.125f, -0.125f,
-0.125f,2, -0.125f,
-0.125f,-0.125f, -0.125f };
Kernel kernel = new Kernel(3, 3, kernelData2);
ConvolveOp cOp = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
mBufferedImage = cOp.filter(mBufferedImage, null);

FileOutputStream out = new FileOutputStream(picUrl + toFileName);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(mBufferedImage);
param.setQuality(quality, true);//默认0.75
encoder.setJPEGEncodeParam(param);
encoder.encode(mBufferedImage);
out.close();
long end = System.currentTimeMillis();
System.out.println("图片:"+fileName+",压缩时间:"+(end - start) + "ms");
}catch (FileNotFoundException fnfe){
fnfe.printStackTrace();
}catch (IOException ioe){
ioe.printStackTrace();
}catch(Exception ex){
ex.printStackTrace();
}
}


调用实例
    public static void main(String[] args){
PicUtil.imageCompress("D:\\", "background_new.png", "background_new2.png", 0.9f, 0.9f);
}
步进电机是将电脉冲信号转变为角位移或线位移的开环控制电机,是现代数字程序控制系统中的主要执行元件,应用极为广泛。在非超载的情况下,电机的转速、停止的位置只取决于脉冲信号的频率和脉冲数,而不受负载变化的影响,当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度,称为“步距角”,它的旋转是以固定的角度一步一步运行的。可以通过控制脉冲个数来控制角位移量,从而达到准确定位的目的;同时可以通过控制脉冲频率来控制电机转动的速度和加速度,从而达到调速的目的。 步距角:对应一个脉冲信号,电机转子转过的角位移用θ表示。θ=360度/(转子齿数运行拍数),以常规二、四相,转子齿为50齿电机为例。四拍运行时步距角为θ=360度/(504)=1.8度(俗称整步),八拍运行时步距角为θ=360度/(50*8)=0.9度(俗称半步)。 转速(Rotational Speed或Rev):是做圆周运动的物体单位时间内沿圆周绕圆心转过的圈数(与频率不同)。常见的转速有额定转速和最大转速等。 硬盘转速以每分钟多少转来表示,单位表示为RPM,RPM是Revolutions Per minute的缩写,是转/每分钟。RPM值越大,内部传输率就越快,访问时间就越短,硬盘的整体性能也就越好。 S型曲线的的方程: y=1/(1+exp(-x)); x在[-5,5]的图形如下图所示: 在这里插入图片描述 在Matlab软件中代码如下: x=-5:0.01:5; y=1./(1+exp(-x)); plot(x,y); 如要将此曲线应用在步进电机的加、减速过程中,需要将方程在XY坐标系进行平移,同时对曲线进行拉升变化: Y=A+B/(1+exp(-ax+b)); 其中的A分量在y方向进行平移,B分量在y方向进行拉伸,ax+b分量在x方向进行平移和拉伸。 其中的A分量在y方向进行平移,B分量在y方向进行拉伸,ax+b分量在x方向进行平移和拉伸。 项目中加速过程:从600Hz加速到20000Hz,采用4细分。输出比较模块所用的定时器驱动频率为10M,采用1000个点进行加速处理。最终根据项目的需要,在加速过程中采用的曲线方程为: Fcurrent = Fmin + (Fmax-Fmin)/(1+exp( -flexible(i - num )/num) );* 步进电机S型曲线加减速算法与实现。 其中的Fcurrent为length(1000)个点中的单个频率值。Fmin起始频率为600; Fmax为最大频率20000; -flexible*(i - num)/num是对S型曲线进行拉伸变化,其中flexible代表S曲线区间(越大代表压缩的最厉害,中间(x坐标0点周围)加速度越大;越小越接近匀加速。理想的S曲线的取值为4-6),i是在循环计算过程中的索引,从0开始,num为 length/2 大小(这样可以使得S曲线对称)。在项目中i的区间[0,1000), num=1000/2=500。这些参数均可以修改。 根据上述文字对此代码/*********************这部分是MOTOR.H文件***********************/ #define ADDSPEEDNUM 500 //加速度脉冲数 #pragma pack(1) typedef struct _FreSpeed { uint16_t MaxPeriod; //最大速度比较器寄存器值 uint32_t MaxNum; //最大速度脉冲数 uint16_t IncPeriod[ADDSPEEDNUM]; //加减速脉冲比较器寄存器值 uint32_t IncNum; //加减速脉冲数 uint8_t AvrFlag; //匀速标志 uint8_t CurState; //当前状态 }FreSpeed; #pragma pack() enum SpeedFlagType { IncSpeedState, //加速状态 DecSpeedState, //加速状态 AvrSpeedState //加速状态 }; #define TIM2_Prescaler 0 #define TIM2_ClockDiv 0 /********************************************/ void STimer2_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // timer2_ch1 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;//PA0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* Time base configuration */ TIM_TimeBaseStructure.TIM_Period = 65535; TIM_TimeBaseStructure.TIM_Prescaler = TIM2_Prescaler; //72/(0+1) = 72M TIM_TimeBaseStructure.TIM_ClockDivision = TIM2_ClockDiv;//0 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 60000; //(72M/60000/2)=600Hz TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//must TIM_OC1Init(TIM2, &TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable); UNMOTORY_TEAR;//使能电机 } void TIM2_IRQHandler(void) { uint16_t capture; static uint16_t iCount=0; static uint8_t SpeedMode = IncSpeedState; /* enter interrupt */ if ( TIM_GetITStatus(TIM2 , TIM_IT_CC1) != RESET ) { TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); capture = TIM_GetCapture1(TIM2); switch(SpeedMode) { case IncSpeedState: TIM_SetCompare1(TIM2, capture + MotorSpeed.IncPeriod[iCount++]); if(iCount == MotorSpeed.IncNum-1) { //rt_kprintf("Tim2 IRQ AddSpeed\n"); SpeedMode = ( MotorSpeed.AvrFlag == RT_TRUE )?AvrSpeedState:DecSpeedState; } break; case AvrSpeedState: TIM_SetCompare1(TIM2, capture + MotorSpeed.MaxPeriod); MotorSpeed.MaxNum--; if(MotorSpeed.MaxNum ==0) { //rt_kprintf("Tim2 IRQ AvrSpeed\n"); SpeedMode = DecSpeedState; } break; case DecSpeedState: TIM_SetCompare1(TIM2, capture + MotorSpeed.IncPeriod[iCount--]); if(iCount==0) { //rt_kprintf("Tim2 IRQ DecSpeed\n"); SpeedMode = IncSpeedState; Set_Timer2_Isr_Disable(); } break; default: SpeedMode = IncSpeedState; //rt_kprintf("Tim2 IRQ Error\n"); break; } } } static void Set_Timer2_Isr_Enable(void) { ENMOTORY_TEAR; TIM_ClearFlag(TIM2, TIM_IT_CC1); TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE); //´ò¿ªÖÐ¶Ï TIM_Cmd(TIM2, ENABLE); } static void Set_Timer2_Isr_Disable(void) { //UNMOTORY_TEAR; TIM_Cmd(TIM2, DISABLE); TIM_ITConfig(TIM2, TIM_IT_CC1, DISABLE); } /************************************************************************ * ------------------------------------------------------------------------------------ * 2018/12/18 V1.0 CCPY 实现S型加减速算法 ************************************************************************/ uint8_t CalculateSModelLine(FreSpeed *iSpeed, float len,uint32_t PulseNum, float fre_min,float fre_max, float flexible) { int i; float TimClock= 72000000.0/(TIM2_Prescaler+1)/(TIM2_ClockDiv+1); float deno; float melo; float delt = fre_max-fre_min; if(delt<=0) { rt_kprintf("fre_max error\n"); return RT_FALSE; } if((PulseNum -2*len)>0) //存在匀速运动 { iSpeed->AvrFlag = RT_TRUE; iSpeed->MaxPeriod = (uint16_t)( TimClock/ (fre_max) / 2); iSpeed->MaxNum = PulseNum -2*len; iSpeed->IncNum = len; } else //不存在匀速运动 { len = PulseNum/2.0; iSpeed->AvrFlag = RT_FALSE; iSpeed->MaxPeriod = RT_FALSE; iSpeed->MaxNum = RT_FALSE; iSpeed->IncNum = len; } for(i=0; i<len; i++ ) { melo = flexible * (i-len/2) / (len/2); deno = 1.0 / (1 + exp(-melo)); iSpeed->IncPeriod[i] = (uint16_t)(TimClock / (delt * deno + fre_min) / 2); } return RT_TRUE; } static void SMoveTrans(float iMaxVel,int32_t PulseNum,float flexible) { float fre_min = 600; //最小速度600hz // float flexible = 8; float Addlen =ADDSPEEDNUM; if(CalculateSModelLine(&MotorSpeed,Addlen,PulseNum,fre_min,iMaxVel,flexible) ==RT_TRUE) { rt_kprintf("Calculate OK\n"); Set_Timer2_Isr_Enable(); } else { rt_kprintf("Calculate Error\n"); } } void ST2Contrel(u32 cnt) { SMoveTrans(5000,cnt,8); Set_Timer2_Isr_Enable(); } 对此代码的每一行标明详细注释并且对此原理做出解释
09-18
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值