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

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

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

前言

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

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


效果圖.png

 

jni_278KB.png

quality_484KB.png

sample_199KB.png

size_238KB.png

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

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

首先了解兩個(gè)圖像處理庫(kù):libjpeg、Skia。

Skia:圖像處理引擎,Google在Android系統(tǒng)上就是采用Skia,它是基于libjpeg的二次封裝,Google在很多其它產(chǎn)品也使用了這個(gè)庫(kù),比如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ù)為T(mén)RUE時(shí),圖片壓縮算法使用最優(yōu)的哈夫曼編碼表,它需要額外傳遞數(shù)據(jù),因此會(huì)耗費(fèi)CPU運(yùn)算時(shí)間,以及開(kāi)辟很多臨時(shí)內(nèi)存空間。
  • 參數(shù)為FALSE時(shí),使用默認(rèn)的哈夫曼編碼表。在大多數(shù)情況,使用最優(yōu)哈夫曼編碼表相比默認(rèn)哈夫曼編碼表,能節(jié)省圖像文件很大比例的大小。

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

哈夫曼樹(shù)和哈夫曼編碼

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

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

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

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

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

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

哈夫曼樹(shù)構(gòu)造

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

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

哈夫曼樹(shù)應(yīng)用

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

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

示例:

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

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

這樣算下來(lái)使用224二進(jìn)制位就可以將該字符串存儲(chǔ)起來(lái),因?yàn)楣蚵a是前綴碼,所以可以唯一的還原出原來(lái)的字符序列。如果我們每個(gè)字符使用3位進(jìn)行存儲(chǔ)(至少3位),那么需要300bit才能將該字符串存儲(chǔ)下。

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

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

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

如果采用定長(zhǎng),那一個(gè)圖片下來(lái)一個(gè)人像素就是001010011100...

如果我們使用可變長(zhǎng)編碼方式,遍歷、再嵌套遍歷,再嵌套遍歷每一個(gè)像素來(lái)獲取前綴編碼(沒(méi)錯(cuò),這個(gè)運(yùn)算過(guò)程很大,而且臨時(shí)變量?jī)?nèi)存也需要很大,但相對(duì)于今天的手機(jī)CPU來(lái)說(shuō),so easy),我們大致可以得到這樣的編碼:

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

那一個(gè)圖片下來(lái)一個(gè)人像素就是011011100...

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

通過(guò)上面的介紹,可以知道最優(yōu)哈夫曼編碼其實(shí)是使用了可變長(zhǎng)編碼方式,而默認(rèn)的哈夫曼編碼使用了定長(zhǎng)編碼方式,因此需要更多的存儲(chǔ)空間,呈現(xiàn)出來(lái)的手機(jī)圖片自然會(huì)大很大。

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

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

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

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

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

實(shí)現(xiàn)的步驟:

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

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

2.導(dǎo)入libjpeg的聲明頭文件,因?yàn)椴襟E1的libjpegbither.so是對(duì)這些頭文件的實(shí)現(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的類(lèi)型

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

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

5.編寫(xiě)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);
// 打開(kāi)輸出文件 wb:可寫(xiě)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);
// 開(kāi)始?jí)嚎s,寫(xiě)入全部像素
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庫(kù)

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

參考鏈接:

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

總結(jié)

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

相關(guān)文章

  • kotlin使用建造者模式自定義對(duì)話框

    kotlin使用建造者模式自定義對(duì)話框

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

    Flutter路由框架Fluro使用教程詳細(xì)講解

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

    android studio 3.0 service項(xiàng)目背景音樂(lè)實(shí)現(xiàn)

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

    Android APK反編譯圖文教程

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

    Android WebView 詳解及簡(jiǎn)單實(shí)例

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

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

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

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

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

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

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

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

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

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

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

最新評(píng)論