curzibn / luban Goto Github PK
View Code? Open in Web Editor NEWLuban(鲁班)—Image compression with efficiency very close to WeChat Moments/可能是最接近微信朋友圈的图片压缩算法
License: Apache License 2.0
Luban(鲁班)—Image compression with efficiency very close to WeChat Moments/可能是最接近微信朋友圈的图片压缩算法
License: Apache License 2.0
320310 4k 的图片 压缩后变成 320310 53k
新版添加多图片同时处理功能,方法名如下
public Luban load(List files)
怎么没找到
项目中要上传一张微博长截图,640*11792的尺寸。
while (stream.toByteArray().length / 1024 > size) {
Log.d("Luban","quality options:"+options);
stream.reset();
options -= 6;
bitmap.compress(Bitmap.CompressFormat.JPEG, options, stream);
}
此处循环耗时严重,最后报错:参数quality 在0-100范围。
Caused by: java.lang.IllegalArgumentException: quality must be 0..100
现在单张压缩基本是满足不了需求的,必须得加入多张压缩,传入list集合的path,还得设置输出的路径,这样才符合需求
setFilename方法必须设置全路径才可以,也就是说必须带上mCacheDir.getAbsolutePath(),而不是仅仅赋文件名就可以。
个人是不想用rxjava得,为了一个压缩引用rxjava和rxAndroid 会增大APK大大小,建议分离rxjava,出一个rxjava版本和普通版本。
请问能不能出一个版本取消Rajava的,并不是所有的人都偏向于使用Rxjava。
thirdCompress方法中
if (scale <= 1 && scale > 0.5625) {
if (height < 1664) {
size = (width * height) / Math.pow(1664, 2) * 150;
size = size < 60 ? 60 : size;
...
}
为什么要用0.5625、1664等数值进行判断?
Luban.get(this)
.load(file) //传人要压缩的图片
.putGear(Luban.THIRD_GEAR) //设定压缩档次,默认三挡
.setCompressListener(new OnCompressListener() {
@OverRide
public void onStart() {
}
@Override
public void onSuccess(File file) {
}
@Override
public void onError(Throwable e) {
}
}).launch(); //启动压缩
我这里提示onStart跟onError不能用@OverRide啊
java.lang.OutOfMemoryError
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:639)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:615)
at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:391)
at top.zibin.luban.Luban.compress(Luban.java:344)//问题在这
at top.zibin.luban.Luban.compress(Luban.java:386)
at top.zibin.luban.Luban.thirdCompress(Luban.java:240)
at top.zibin.luban.Luban.access$200(Luban.java:24)
at top.zibin.luban.Luban$10.call(Luban.java:178)
at top.zibin.luban.Luban$10.call(Luban.java:175)
at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:54)
at rx.internal.util.ScalarSynchronousObservable$1.call(ScalarSynchronousObservable.java:46)
at rx.internal.util.ScalarSynchronousObservable$1.call(ScalarSynchronousObservable.java:35)
at rx.Observable$2.call(Observable.java:162)
at rx.Observable$2.call(Observable.java:154)
at rx.Observable$2.call(Observable.java:162)
at rx.Observable$2.call(Observable.java:154)
at rx.Observable.unsafeSubscribe(Observable.java:8098)
at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:841)
看到最新支持了多文件的压缩处理..
那么,当同时进行8个图片的压缩的时候,
势必会出现8个bitmap在内存..8个bitmap同时在进行循环压缩到指定大小...
那么, 很容易就会出现out of memory.
有没有一种方式.扔出8张图片, 以队列的形式每次压缩2张左右.分4次压缩完成.
缓存的文件名直接是时间戳了。
希望能改一下这个地方
1、我最近在找工作,Luban 也是这两天突然爆发起来,如果有问题请各位包涵
2、图片压缩是安卓开发的刚需,我希望能靠大家一起讨论完善 Luban
3、如果想把 Luban 用上线上产品的大大请直接用源码,并针对自家产品优化,不要因为 Luban 而对产品造成影响
4、我会不断花时间优化 Luban 项目,希望大家多多支持
5、想加入讨论的大家是希望建Q群,微信群,还是继续在 issues 中讨论,可以在本 issues 下发表意见
Error:错误: 不兼容的类型: OnCompressListener 不是函数接口
在 接口 OnCompressListener 中找到多个非覆盖抽象方法
注: 某些输入文件使用或覆盖了已过时的 API。
注: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。
注: 某些输入文件使用了未经检查或不安全的操作。
注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。
注: 某些消息已经过简化; 请使用 -Xdiags:verbose 重新编译以获得完整输出
1 个错误
FAILURE: Build completed with 2 failures.
What went wrong:
Execution failed for task ':app:compileDebugJavaWithJavac'.
Compilation failed; see the compiler error output for details.
Try:
What went wrong:
Execution failed for task ':app:compileRetrolambdaDebug'.
Process 'command '/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/bin/java'' finished with non-zero exit value 1
Try:
看了你写的代码,OnCompressListener的onSuccess方法是在try catch里的,既然这样说明可能会有失败,那么加个onError接口应该是极好的
这个算法确实不错。
但是Luban.FIRST_GEAR时,有的图片会报错,0..100压缩比,超出界限。
repo主的这项目棒极了,昨天把rxjava扣掉之后,今天整理了下,把这部分代码剥离出来做了个demo
红米Note 2 上一张8.05M的图片出现这个错误,小点得图片没事,求指导
能否给出混淆代码
如果不在项目中不使用这个RxJava和RxAndroid,那太冗余了,自己要根据算法修改源码了
新版添加多图片同时处理功能,方法名如下
public Luban load(List<File> files){ //TODO compress image list }
Luban.get(activity).load(file).putGear(Luban.THIRD_GEAR).asObservable().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
运行压缩后界面会卡主,主线程被阻塞。subscribeOn方法好像无效
在压缩之前显示progressdialog显示不出来 好像是ui线程阻塞了 # @Curzibn
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int android.graphics.Bitmap.getWidth()' on a null object reference
我们知道一张A压缩过后的结果A_COMPRESS会保存在CACHE结果.
那么下一次我们压缩A的时候,可以直接返回A_COMPRESS.直接跳过压缩流程.
当然也可以设置一个FLAG用来设置是否使用上一次压缩结果.
采用的方式是,从相册选择图片发送朋友圈,等待一时,压缩完成后,保存到相册。
我上传了一张1300w-4:3的图片,luban 是长宽各压缩1/2,微信是的1280*960。
又测试了一张1080P 的截图,luban 尺寸保持1080P,微信是的720P。
因为图片尺寸大了很多,所以在同等大小的情况下,luban 的画质损耗程度完全不可接受。
不知道为什么和作者的测试数据差那么多,是我的测试方法和作者不一致吗?
rt,我是这么循环压缩的
for(String fileUrl : fileList){
Luban.get(this).putGear(Luban.THIRD_GEAR)
.load(new File(fileUrl))
// .setFilename(getCurrentPhotoFile().getAbsolutePath())
.setCompressListener(new OnCompressListener() {
@Override
public void onSuccess(File file) {
DLog.i("完成剪裁:" + file.getAbsolutePath());
mCompressList.add(file.getAbsolutePath());
if(mCompressList.size() == fileList.size()){
uploadFile(mCompressList);
}
}
@Override
public void onStart() {
DLog.i("开始剪裁");
}
@Override
public void onError(Throwable e) {
DLog.i("error:" + e.toString());
}
}).launch();
结果发现剪裁完的地址可能会出现相同的结果。
public static void compressImage(final String path,final CountDownLatch latch,final int number){
Luban.get(BaseApplication.mContext)
.load(new File(path)) //传人要压缩的图片
.putGear(Luban.THIRD_GEAR) //设定压缩档次,默认三挡
.setCompressListener(new OnCompressListener() { //设置回调
@Override
public void onStart() {
//TODO 压缩开始前调用,可以在方法内启动 loading UI
Logger.e("*********onStart Compress***************");
}
@Override
public void onSuccess(File file) {
//TODO 压缩成功后调用,返回压缩后的图片文件
Logger.e("*********onSuccess Compress***************");
uploadSingleImage(file.getPath(),latch,number);
}
@Override
public void onError(Throwable e) {
//TODO 当压缩过去出现问题时调用
Logger.e("*********onError Compress***************");
}
}).launch(); //启动压缩
}
return BitmapFactory.decodeFile(imagePath, options);
归根结底就这一句话而已
必现, 或许和图片选择库有一定的关系, 滑到最底部, 选择那张图片, 点完成, 会oom;
08-04 12:05:12.904 32005-32005/top.zibin.luban.example E/dalvikvm-heap: Out of memory on a 13128976-byte allocation.
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: "main" prio=5 tid=1 RUNNABLE
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: | group="main" sCount=0 dsCount=0 obj=0x4161ee58 self=0x4153b518
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: | sysTid=32005 nice=0 sched=0/0 cgrp=apps handle=1074413908
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: | state=R schedstat=( 6918152497 1493630453 7279 ) utm=644 stm=47 core=2
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:627)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:603)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:378)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at top.zibin.luban.Luban.compress(Luban.java:317)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at top.zibin.luban.Luban.compress(Luban.java:359)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at top.zibin.luban.Luban.thirdCompress(Luban.java:213)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at top.zibin.luban.Luban.asObservable(Luban.java:158)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at top.zibin.luban.example.MainActivity.compressWithRx(MainActivity.java:96)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at top.zibin.luban.example.MainActivity.onActivityResult(MainActivity.java:134)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at android.app.Activity.dispatchActivityResult(Activity.java:5434)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at android.app.ActivityThread.deliverResults(ActivityThread.java:3363)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at android.app.ActivityThread.handleSendResult(ActivityThread.java:3410)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at android.app.ActivityThread.access$1300(ActivityThread.java:141)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1260)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at android.os.Handler.dispatchMessage(Handler.java:102)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at android.os.Looper.loop(Looper.java:136)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at android.app.ActivityThread.main(ActivityThread.java:5113)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at java.lang.reflect.Method.invokeNative(Native Method)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at java.lang.reflect.Method.invoke(Method.java:515)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:609)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example I/dalvikvm: at dalvik.system.NativeStart.main(Native Method)
08-04 12:05:12.904 32005-32005/top.zibin.luban.example D/skia: --- decoder->decode returned false
08-04 12:05:12.904 32005-32005/top.zibin.luban.example D/AndroidRuntime: Shutting down VM
08-04 12:05:12.904 32005-32005/top.zibin.luban.example W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x4161dd58)
代码显示这个地方乘的是400? else if (scale <= 0.5625 && scale > 0.5) {
if (height < 1280 && file.length() / 1024 < 200) return file;
int multiple = height / 1280 == 0 ? 1 : height / 1280;
thumbW = width / multiple;
thumbH = height / multiple;
size = (thumbW * thumbH) / (1440.0 * 2560.0) * 400;
size = size < 100 ? 100 : size;
}
作者能解释下,到底是多少不?
连续放9张3-4MB的原图,压缩过程就有点长了,而且还要上传,很磨人啊
int thumbW = width % 2 == 1 ? width + 1 : width; 为什么要这样写
你好,请问你在完成此项目的过程中,研究过微信对话界面,图片显示到对话界面上的预览图的大小缩放规律吗?
红米1s拍的照片,压缩之后部分照片会出现比较严重的颜色失真
public void dealImg(ArrayList fileList){
files.clear();
for (int i = 0; i < fileList.size(); i++) {
File file = fileList.get(i);
Luban.get(this)
.load(file) //传人要压缩的图片
.putGear(Luban.THIRD_GEAR) //设定压缩档次,默认三挡
.setCompressListener(new OnCompressListener() {
@OverRide
public void onStart() {
}
@Override
public void onSuccess(File file) {
files.add(file);
}
public void onError(Throwable e) {
}
}).launch(); //启动压缩
}
}
java.lang.NoClassDefFoundError: Failed resolution of: Lrx/Observable;
at top.zibin.luban.Luban.launch(Luban.java:113)
at com.xinji.jugongyouyue.workeractivity.WorkerActivityAddProject.dealImg(WorkerActivityAddProject.java:304)
at com.xinji.jugongyouyue.workeractivity.WorkerActivityAddProject.onActivityResult(WorkerActivityAddProject.java:296)
at android.app.Activity.dispatchActivityResult(Activity.java:6378)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3970)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4017)
at android.app.ActivityThread.access$1300(ActivityThread.java:178)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1602)
at android.os.Handler.dispatchMessage(Handler.java:111)
at android.os.Looper.loop(Looper.java:192)
at android.app.ActivityThread.main(ActivityThread.java:5784)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1084)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:850)
Caused by: java.lang.ClassNotFoundException: Didn't find class "rx.Observable" on path: DexPathList[[zip file "/data/app/com.xinji.jugongyouyue-1/base.apk"],nativeLibraryDirectories=[/data/app/com.xinji.jugongyouyue-1/lib/arm, /vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at top.zibin.luban.Luban.launch(Luban.java:113)
at com.xinji.jugongyouyue.workeractivity.WorkerActivityAddProject.dealImg(WorkerActivityAddProject.java:304)
at com.xinji.jugongyouyue.workeractivity.WorkerActivityAddProject.onActivityResult(WorkerActivityAddProject.java:296)
at android.app.Activity.dispatchActivityResult(Activity.java:6378)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3970)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4017)
at android.app.ActivityThread.access$1300(ActivityThread.java:178)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1602)
at android.os.Handler.dispatchMessage(Handler.java:111)
at android.os.Looper.loop(Looper.java:192)
at android.app.ActivityThread.main(ActivityThread.java:5784)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1084)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:850)
Suppressed: java.lang.ClassNotFoundException: rx.Observable
at java.lang.Class.classForName(Native Method)
at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
... 16 more
Rejecting re-init on previously-failed class java.lang.Class<top.zibin.luban.Luban$3>
Process: com.mayi.android_learning, PID: 7575
java.lang.IllegalArgumentException: quality must be 0..100
at android.graphics.Bitmap.compress(Bitmap.java:1017)
at top.zibin.luban.Luban.saveImage(Luban.java:404)
at top.zibin.luban.Luban.compress(Luban.java:363)
at top.zibin.luban.Luban.firstCompress(Luban.java:253)
at top.zibin.luban.Luban.launch(Luban.java:90)
at com.mayi.android_learning.activity.LuBanActivity.handlerImg(LuBanActivity.java:98)
at com.mayi.android_learning.activity.LuBanActivity.onClick(LuBanActivity.java:51)
at com.mayi.android_learning.activity.LuBanActivity$$ViewBinder$1.doClick(LuBanActivity$$ViewBinder.java:24)
at butterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:22)
当图片本身大小很小的时候会出现压缩后图片变大的结果
横版长图压缩完全模糊……
您这个是图片上传的时候,本地图片压缩吗?
我之前也研究过微信朋友圈的图片压缩,当时得到的结果是:
第一步进行采样率压缩;
第二步进行宽高的等比例压缩(微信对原图和缩略图限制了最大长宽或者最小长宽);
第三步就是对图片的质量进行压缩(一般75或者70);
第四部就是采用webP的格式。
经过这四部的处理,基本上和微信朋友圈的效果一致,包括文件大小和显示效果。
(PS:最近看到微信朋友圈好像换了图片格式,这还有待考证)
长图压缩有两个bug一个option会出现小于0
thirdCompress
else if (c <= 0.5625 && c > 0.5) { int multiple = j / 1280; k = i / multiple; l = j / multiple; s = (k * l) / (1440.0 * 2560.0) * 200; s = s < 100 ? 100 : s;
multiple有可能为 0
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.