欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JNI方法實現(xiàn)圖片壓縮(壓縮率極高)

 更新時間:2018年11月14日 09:59:51   作者:_Ricky_  
這篇文章主要給大家介紹了一種JNI方法實現(xiàn)圖片壓縮(壓縮率極高)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對各位Android開發(fā)者們具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧

前言

直接使用項目或直接復(fù)制libs中的so庫到項目中即可(當前只構(gòu)建了armeabi),需要其他ABI可檢下項目另外使用CMake構(gòu)建即可。

結(jié)果預(yù)覽:


效果圖.png

 

jni_278KB.png

quality_484KB.png

sample_199KB.png

size_238KB.png

原圖大小5.99M~~ 我們把所有經(jīng)過壓縮的圖片放到同等大小的情況后,很明顯,采樣壓縮跟尺寸壓縮都不是我們想要的結(jié)果,而質(zhì)量壓縮跟JNI壓縮我設(shè)置的質(zhì)量壓縮值都是30,JNI壓縮出來只有278KB,直接質(zhì)量壓縮出來的有484KB,綜合之后,JNI才是綜合最優(yōu)的方式,當然,如果只是頭像,我們設(shè)置可以把配置值設(shè)置得更小,圖片就更小。

為什么iPhone手機圖片的質(zhì)量比Android的好?

首先了解兩個圖像處理庫:libjpeg、Skia。

Skia:圖像處理引擎,Google在Android系統(tǒng)上就是采用Skia,它是基于libjpeg的二次封裝,Google在很多其它產(chǎn)品也使用了這個庫,比如Chorme,F(xiàn)irefox等等。

libjpeg:早期的圖像處理引擎,用于PC端。

官方文檔可以看到libjpeg.doc這樣一段話:

boolean optimize_coding
TRUE causes the compressor to compute optimal Huffman coding tables
for the image. This requires an extra pass over the data and
therefore costs a good deal of space and time. The default is
FALSE, which tells the compressor to use the supplied or default
Huffman tables. In most cases optimal tables save only a few percent
of file size compared to the default tables. Note that when this is
TRUE, you need not supply Huffman tables at all, and any you do
supply will be overwritten.

boolean optimize_coding:

  • 參數(shù)為TRUE時,圖片壓縮算法使用最優(yōu)的哈夫曼編碼表,它需要額外傳遞數(shù)據(jù),因此會耗費CPU運算時間,以及開辟很多臨時內(nèi)存空間。
  • 參數(shù)為FALSE時,使用默認的哈夫曼編碼表。在大多數(shù)情況,使用最優(yōu)哈夫曼編碼表相比默認哈夫曼編碼表,能節(jié)省圖像文件很大比例的大小。

為什么使用最優(yōu)哈夫曼編碼表可以節(jié)省圖像文件很大的比例大小呢?

哈夫曼樹和哈夫曼編碼

當樹中的節(jié)點被賦予一個表示某種意義的數(shù)值,我們稱之為該節(jié)點的權(quán)。從樹的根節(jié)點到任意節(jié)點的路徑長度(經(jīng)過的邊數(shù))與該節(jié)點上權(quán)值的乘積稱為該節(jié)點的帶權(quán)路徑長度。樹中所有葉節(jié)點的帶權(quán)路徑長度之和稱為該樹的帶權(quán)路徑長度(WPL)。當帶權(quán)路徑長度最小的二叉樹被稱為哈夫曼樹,也成為最優(yōu)二叉樹。

如下圖所示,有三課二叉樹,每個樹都有四個葉子節(jié)點a,b,c,d,分別取帶權(quán)7,5,2,4。他們的帶權(quán)路徑長度分別為

(a) WPL = 7x2+5x2+2x2+4x2=36

(b) WPL = 2X1+4X2+7X3+5X3 = 46

(c) WPL = 7x1+5x2+2x3+4x3 = 35

節(jié)點如果像c中的方式分布的話,WPL能取最小值(可證明),我們稱為哈夫曼樹。

哈夫曼樹構(gòu)造

哈夫曼樹在構(gòu)造時每次從備選節(jié)點中挑出兩個權(quán)值最小的節(jié)點進行構(gòu)造,每次構(gòu)造完成后會生成新的節(jié)點,將構(gòu)造的節(jié)點從備選節(jié)點中刪除并將新產(chǎn)生的節(jié)點加入到備選節(jié)點中。新產(chǎn)生的節(jié)點權(quán)值為參與構(gòu)造的兩個節(jié)點權(quán)值之和。舉例如下:

  • 備選節(jié)點為a,b,c,d,權(quán)值分別為7,5,2,4
  • 選出c和d進行構(gòu)造(權(quán)值最小),生成新節(jié)點為e(權(quán)值為6),備選節(jié)點變?yōu)?,5,6
  • 選出b和e進行構(gòu)造,生成新節(jié)點f(權(quán)值為11),備選節(jié)點為7,11
  • 將最后的7和11節(jié)點進行構(gòu)造,最后生成如圖所示的哈夫曼樹

哈夫曼樹應(yīng)用

在處理字符串序列時,如果對每個字符串采用相同的二進制位來表示,則稱這種編碼方式為定長編碼。若允許對不同的字符采用不等長的二進制位進行表示,那么這種方式稱為可變長編碼??勺冮L編碼其特點是對使用頻率高的字符采用短編碼,而對使用頻率低的字符則采用長編碼的方式。這樣我們就可以減少數(shù)據(jù)的存儲空間,從而起到壓縮數(shù)據(jù)的效果。而通過哈夫曼樹形成的哈夫曼編碼是一種的有效的數(shù)據(jù)壓縮編碼。

如果沒有一個編碼是另一個編碼的前綴,則稱這樣的編碼為前綴編碼。如0,101和100是前綴編碼。由前綴碼形成的序列可以被唯一的組成一個字符串序列。如00101100可以被唯一的分析為0,0,101和100。

示例:

我們對一個字符串進行統(tǒng)計發(fā)現(xiàn)a-f出現(xiàn)的頻率分別為a:45,b:13,c:12,d:16,e:9,f:5,我們對該字符串進行采用哈夫曼編碼進行存儲。

WPL = 1x45+3x(13+12+16)+4x(5+9)=224

這樣算下來使用224二進制位就可以將該字符串存儲起來,因為哈夫曼碼是前綴碼,所以可以唯一的還原出原來的字符序列。如果我們每個字符使用3位進行存儲(至少3位),那么需要300bit才能將該字符串存儲下。

其次了解下libjpeg使用哈夫曼編碼是對圖片上的每個像素(ARGB)進行編碼,比如

ARGB(每個顏色通道取值范圍0-255)的編碼分別是:

A:001
R:010
G:011
B:100

如果采用定長,那一個圖片下來一個人像素就是001010011100...

如果我們使用可變長編碼方式,遍歷、再嵌套遍歷,再嵌套遍歷每一個像素來獲取前綴編碼(沒錯,這個運算過程很大,而且臨時變量內(nèi)存也需要很大,但相對于今天的手機CPU來說,so easy),我們大致可以得到這樣的編碼:

A:01
R:10
G:11
B:100

那一個圖片下來一個人像素就是011011100...

編碼長度的優(yōu)化后,接下來干的事就是計算權(quán)重以及每個顏色通道對應(yīng)的編碼的出現(xiàn)頻次構(gòu)建哈夫曼樹了,這里就參考上面的圖片了。

通過上面的介紹,可以知道最優(yōu)哈夫曼編碼其實是使用了可變長編碼方式,而默認的哈夫曼編碼使用了定長編碼方式,因此需要更多的存儲空間,呈現(xiàn)出來的手機圖片自然會大很大。

libjpeg把optimize_coding參數(shù)默認設(shè)置為FALSE是因為10多年前的Android手機CPU跟內(nèi)存都非常吃緊,所以當年沒有設(shè)置為TRUE。如今的手機CPU跟內(nèi)存都“起飛了”,Goolge的Skia圖像處理引擎卻還是使用optimize_coding的默認值FALSE。我們無法修改系統(tǒng)Skia的這個參數(shù)值,所以只能默默忍受size很大的圖像文件。

經(jīng)過大量圖像壓縮測試結(jié)果,得到兩個結(jié)論:

1.圖片壓縮到相同的質(zhì)量,F(xiàn)ALSE所產(chǎn)出的圖像文件大小是TRUE的5-10倍。

2.圖片壓縮到相同的質(zhì)量,Android所產(chǎn)出的圖像文件大小比iOS也是大5-10倍。

所以,通過使用libjpeg編譯自己的native library修改optimize_coding參數(shù)的值,達圖像質(zhì)量相同,所產(chǎn)出的圖像卻能節(jié)省5-10倍空間大小的效果。

實現(xiàn)的步驟:

1.構(gòu)建libjpeg的so庫

到官方下載對應(yīng)自己電腦系統(tǒng)類型的壓縮包,創(chuàng)建Android項目導(dǎo)入壓縮包里頭的xx.h、xx.c文件構(gòu)建so庫。bither/bither-android-lib已經(jīng)做了這個工作,因此我們只需直接拿他的libjpegbither.so即可。

2.導(dǎo)入libjpeg的聲明頭文件,因為步驟1的libjpegbither.so是對這些頭文件的實現(xiàn),因此需要導(dǎo)入這些頭文件。

3.創(chuàng)建CMake腳本

cmake_minimum_required(VERSION 3.4.1)

add_library( effective-bitmap
SHARED
src/main/cpp/effective-bitmap.c )


include_directories( src/main/cpp/jpeg/
)

add_library(jpegbither SHARED IMPORTED)
set_target_properties(jpegbither
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libjpegbither.so)


find_library( log-lib
log )

find_library( jnigraphics-lib jnigraphics )

target_link_libraries( effective-bitmap
jpegbither
${log-lib}
${jnigraphics-lib})

4.配置gradle關(guān)聯(lián)CMakeLists.txt構(gòu)建腳本,以及指定產(chǎn)出的ABI的類型

android {
// ...
defaultConfig {
// ...
externalNativeBuild {
cmake {
cppFlags ""
}
}

ndk {
abiFilters 'armeabi'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}

5.編寫C/C++代碼

int generateJPEG(BYTE* data, int w, int h, int quality,
const char* outfilename, jboolean optimize) {
int nComponent = 3;
// jpeg的結(jié)構(gòu)體,保存的比如寬、高、位深、圖片格式等信息
struct jpeg_compress_struct jcs;

struct my_error_mgr jem;

jcs.err = jpeg_std_error(&jem.pub);
jem.pub.error_exit = my_error_exit;
if (setjmp(jem.setjmp_buffer)) {
return 0;
}
jpeg_create_compress(&jcs);
// 打開輸出文件 wb:可寫byte
FILE* f = fopen(outfilename, "wb");
if (f == NULL) {
return 0;
}
// 設(shè)置結(jié)構(gòu)體的文件路徑
jpeg_stdio_dest(&jcs, f);
jcs.image_width = w;
jcs.image_height = h;

// 設(shè)置哈夫曼編碼
jcs.arith_code = false;
jcs.input_components = nComponent;
if (nComponent == 1)
jcs.in_color_space = JCS_GRAYSCALE;
else
jcs.in_color_space = JCS_RGB;

jpeg_set_defaults(&jcs);
jcs.optimize_coding = optimize;
jpeg_set_quality(&jcs, quality, true);
// 開始壓縮,寫入全部像素
jpeg_start_compress(&jcs, TRUE);

JSAMPROW row_pointer[1];
int row_stride;
row_stride = jcs.image_width * nComponent;
while (jcs.next_scanline < jcs.image_height) {
row_pointer[0] = &data[jcs.next_scanline * row_stride];
jpeg_write_scanlines(&jcs, row_pointer, 1);
}

jpeg_finish_compress(&jcs);
jpeg_destroy_compress(&jcs);
fclose(f);

return 1;
}

6.構(gòu)建so庫

源碼:https://github.com/zengfw/EffectiveBitmap (本地下載

參考鏈接:

Why the image quality of iPhone is much better than Android?

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • kotlin使用建造者模式自定義對話框

    kotlin使用建造者模式自定義對話框

    這篇文章主要為大家詳細介紹了kotlin使用建造者模式自定義對話框的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • Flutter路由框架Fluro使用教程詳細講解

    Flutter路由框架Fluro使用教程詳細講解

    在Flutter應(yīng)用開發(fā)過程中,除了使用Flutter官方提供的路由外,還可以使用一些第三方路由框架來實現(xiàn)頁面管理和導(dǎo)航,F(xiàn)luro作為一款優(yōu)秀的Flutter企業(yè)級路由框架,F(xiàn)luro的使用比官方提供的路由框架要復(fù)雜一些,但是卻非常適合中大型項目
    2022-10-10
  • android studio 3.0 service項目背景音樂實現(xiàn)

    android studio 3.0 service項目背景音樂實現(xiàn)

    這篇文章主要介紹了android studio 3.0中service項目實現(xiàn)插入背景音樂的方法。
    2017-11-11
  • Android APK反編譯圖文教程

    Android APK反編譯圖文教程

    學(xué)會反編譯比較關(guān)鍵,也是我們美化必須掌握技術(shù),學(xué)會反編譯也是實現(xiàn)制作ROM的起步,ROM高手必然是反編譯高手這里有必要說一下,教程只是給你一個動手的那一個蹺板,教程不是萬能的,給了你基礎(chǔ)與啟發(fā),最重要的是我們能夠自主的進行創(chuàng)新與思考
    2016-04-04
  • Android WebView 詳解及簡單實例

    Android WebView 詳解及簡單實例

    這篇文章主要介紹了Android WebView 詳解及簡單實例的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • Android開發(fā)教程之如何屏蔽View的重復(fù)點擊

    Android開發(fā)教程之如何屏蔽View的重復(fù)點擊

    這篇文章主要給大家介紹了關(guān)于Android開發(fā)教程之如何屏蔽View的重復(fù)點擊的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧
    2018-09-09
  • Android動態(tài)表格的實現(xiàn)代碼(內(nèi)容、樣式可擴縮)

    Android動態(tài)表格的實現(xiàn)代碼(內(nèi)容、樣式可擴縮)

    這篇文章主要介紹了Android動態(tài)表格的實現(xiàn)代碼(內(nèi)容、樣式可擴縮),本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09
  • Android添加用戶組及自定義App權(quán)限的方法

    Android添加用戶組及自定義App權(quán)限的方法

    今天小編就為大家分享一篇Android添加用戶組及自定義App權(quán)限的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • android中設(shè)置TextView/Button 走馬燈(Marquee)效果示例

    android中設(shè)置TextView/Button 走馬燈(Marquee)效果示例

    定義走馬燈(Marquee),主要在Project/res/layout/main.xml即可,下面與大家分享下具體的實現(xiàn),感興趣的朋友可以參考下哈
    2013-06-06
  • Android Chronometer控件實現(xiàn)計時器函數(shù)詳解

    Android Chronometer控件實現(xiàn)計時器函數(shù)詳解

    這篇文章主要為大家詳細介紹了Android Chronometer控件實現(xiàn)計時器函數(shù),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-04-04

最新評論