吴忠躺衫网络科技有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux應用開發【第二章】圖像處理應用開發

weidongshan ? 來源:weidongshan ? 作者:weidongshan ? 2021-12-10 17:40 ? 次閱讀

文章目錄

2 圖像處理應用開發

2.1 BMP圖像處理

2.1.1 BMP文件格式解析

2.1.2 代碼實現:將BMP文件解析為RGB格式,在LCD上顯示

2.2 JPEG圖像處理

2.2.1 JPEG文件格式和libjpeg編譯

2.2.2 libjpeg接口函數的解析和使用

2.2.3 使用libjpeg把JPEG文件解析為RGB格式,在LCD上顯示

2.3 PNG圖像處理

2.3.1 PNG文件格式和libpng編譯

2.3.2 libpng接口函數的解析和使用

2.3.3 使用libpng把png文件轉為rgb格式,在LCD上顯示

2.4 圖像調整

2.4.1 圖像的縮放

2.4.1.1 圖像縮放算法淺析

2.4.1.2源碼編寫:圖像縮放算法

2.4.2 圖像的旋轉

2.4.2.1 圖像旋轉算法淺析

2.4.2.2 源碼編寫:圖像旋轉算法

2 圖像處理應用開發

前言:所有的圖像文件,都是一種二進制格式文件,每一個圖像文件,都可以通過解析文件中的每一組二進制數的含義來獲得文件中的各種信息,如圖像高度,寬度,像素位數等等。只是不同的文件格式所代表的二進制數含義不一樣罷了。我們可以通過UltraEdit軟件打開圖像文件并查看里面的二進制數排列。

2.1 BMP圖像處理

2.1.1 BMP文件格式解析

BMP是一種常見的圖像格式,BMP文件可看成由4個部分組成:位圖文件頭(bitmap-file header)、位圖信息頭(bitmap-information header)、調色板(color palette)和定義位圖的字節陣列。以最簡單的24位真彩色BMP文件作例子講解:

位圖文件頭(bitmap-file header)

這部分可以理解為是一個結構體,里面的每一個成員都表示一個屬性

位數文件頭由以下信息組成:

bfType 2字節 表明它是BMP格式的文件,
內容固定為0x42,0x4D,
即ASCII字符中的“B”“M”
bfSize 4字節 BMP文件的大小,單位為字節
bfReserved1 2字節 保留
bfReserved2 2字節 保留
名稱 字節數 含義

我們用UltraEdit打開一個BMP文件,可以看到如下信息

pYYBAGGzIK2AUmaKAABE9hwcIE4760.png

這是該BMP文件前32字節的數據,可以看到,前兩個字節分別為0x42,0x4D;

接著后面4個字節依次是0x36,0xF9,0x15,0x00。

在BMP格式中,文件的存儲方式是小端模式,即如果一個數據需要用幾個字節來表示的話,那么,低位數據存在低位地址上,高位數據存在高位地址上。類似的,還有大端模式,即:如果一個數據需要用幾個字節來表示的話,那么,低位數據存在高位地址上,高位數據存在低位地址上。

所以0x36,0xF9,0x15,0x00四個數據拼接方法應該是:0x0015F936(在數字中個位即最右邊才是最低位),它正好就是這個文件的大小:

poYBAGGzILSAOI3JAAAkWqEs2Cs487.png

緊接著是4個保留位字節,其數據必須為0x00。

最后是4個字節的便宜位,可以看到位圖文件頭+位圖信息頭+調色板的大小應該是0x36。

位圖信息頭(bitmap-information header)

位圖信息頭也可以理解為是一個結構體,其成員有:

biSize 4 整個位圖信息頭結構體的大小
biWidth 4 圖像寬度,單位為像素
biHeight 4 圖像高度,單位為像素。 此外,這個數的
正負可以判斷圖像是正向還是倒向的,若為
正,則表示是正向;若為負,則表示反向。
其實根本不同就是坐標系的建立方法不一樣。
后面寫代碼時會講。
biPlanes 2 顏色平面書,其值總為1
biBitCount 2 即1個像素用多少位的數據來表示,其值可
能為1,4,8,16,24,32。我們是以24位
真彩色為例子講解的
biCompression 4 數據的壓縮類型
biSizeImage 4 圖像數據的大小,單位為字節
biXPelsPerMeter 4 水平分辨率,單位是像素/米
biYPelsPerMeter 4 垂直分辨率,單位是像素/米
biClrUsed 4 調色板中的顏色索引數
biClrImportant 4 說明有對圖像有重要影響的顏色索引的數
目,若為0,表示都重要
名稱 字節數 含義

對照源文件數據:

poYBAGGzILqASPwYAAEG26e2AHo295.png

0E-11:00000028h = 40,表示這個結構體大小是40字節。

12-15:00000320h = 800,圖像寬為800像素。

16-19:00000258h = 600,圖像高為600像素,與文件屬性一致。這是一個正數,說明圖像是正向的,數據是以圖像左下角為原點,以水平向右為X軸正方向,以垂直向上為Y軸正方向排列的。若為負,則說明圖像是反向的,數據是以圖像左上角角為原點,以水平向右為X軸正方向,以垂直向下為Y軸正方向排列的。

1A-1B:0001h, 該值總為1。

1C-1D:0018h = 24, 表示每個像素占24個比特,即24位真彩色

上面這幾個信息跟文件屬性是一致的:

poYBAGGzIMCACCZ5AAA7NF7riX4675.png

1E-21:00000000h,BI_RGB, 說明本圖像不壓縮。

22-25:00000000h,圖像的大小,因為使用BI_RGB,所以設置為0。

26-29:00000000h,水平分辨率,缺省。

2A-2D:00000000h,垂直分辨率,缺省。

2E-31:00000000h,對于24位真彩色來說,是沒有調色板的,所以為0。

32-35:00000000h,對于24位真彩色來說,是沒有調色板的,所以為0。

調色板(color palette)

24位真彩色沒有調色板,這里為了簡化不贅訴。

定義位圖的字節陣列

這一部分就是真正的圖像數據了,24位真彩色數據是按按BGR各一字節循環排列而成。

2.1.2 代碼實現:將BMP文件解析為RGB格式,在LCD上顯示

讓BMP文件在開發板的LCD上顯示出來,有幾個需要注意的點:

開發板LCD上的顯示格式是RGB格式的,而且有多種表示格式:可能用2字節表示(RGB565格式),可能用3字節表示(RGB888),而原始的24位真彩色BMP文件則是按BGR格式排列的,需要對原始的圖像數據進行轉化。

在轉化過程中,LCD上的顯存地址固定是以LCD左上角為首地址,而BMP格式中正向圖像是以圖片的左下角為數據首地址的。因此在進行數據轉化時還需要注意坐標的變換。

代碼清單2.1實現了將24位真彩色的BMP圖像轉化為RGB格式

代碼清單2.1 1. /********************************************************************** 2. * 函數名稱: IsBmp 3. * 功能描述: 判斷該文件是否為BMP文件 4. * 輸入參數: ptFileMap - 內含文件信息 5. * 輸出參數: 無 6. * 返 回 值: 0 - 是BMP格式, -1 -不是BMP格式 7. ***********************************************************************/ 8. int IsBmp(FILE **ppFp, const char *strFileName) 9. { 10. char strCheckHeader[2]; 11. *ppFp= fopen(strFileName, "rb+"); 12. if (*ppFp== NULL) { 13. return -1; 14. } 15. if (fread(strCheckHeader, 1, 2, *ppFp) != 2) 16. return -1; 17. 18. if (strCheckHeader[0] != 0x42 || strCheckHeader[1] != 0x4d) 19. return -1; 20. else 21. return 0; 22. } 23. 24. 25. 26. /********************************************************************** 27. * 函數名稱: MapFile 28. * 功能描述: 使用mmap函數映射一個文件到內存,以后就可以直接通過內存來訪問文件 29. * 輸入參數: PT_PictureData ptData 內含圖像數據 30. * 輸出參數: ptData->iFileSize : 文件大小 31. * ptData->pucFileData : 映射內存的首地址 32. * 返 回 值: 0 - 成功其他值 - 失敗 33. ***********************************************************************/ 34. int MapFile(PT_PictureData ptData) 35. { 36. int iFd; 37. struct stat tStat; 38. 39. /* 打開文件 */ 40. iFd = fileno(ptData->ptFp); 41. fstat(iFd, &tStat); 42. ptData->iFileSize= tStat.st_size; 43. ptData->pucFileData= (unsigned char *)mmap(NULL , tStat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, iFd, 0); 44. if (ptData->pucFileData == (unsigned char *)-1) 45. { 46. printf("mmap error!n"); 47. return -1; 48. } 49. return 0; 50. } 51. 52. /********************************************************************** 53. * 函數名稱: DecodeBmp2Rgb 54. * 功能描述:把BMP文件轉化為rgb格式 55. * 輸入參數: strFileName - 文件名 56. * ptData - 內含圖像信息 57. * 返 回 值: 0 - 成功其他值 - 失敗 58. * -1 - 文件不是BMP格式 59. * -2 - 不支持的bpp 60. * -3 - 圖像緩存區分配失敗 61. ***********************************************************************/ 62. static int DecodeBmp2Rgb(const char *strFileName, PT_PictureData ptData) { 63. int x,y; 64. int iPos = 0; 65. int iLineWidthAlign; 66. BITMAPFILEHEADER *ptBITMAPFILEHEADER; 67. BITMAPINFOHEADER *ptBITMAPINFOHEADER; 68. unsigned char *aFileHead; 69. unsigned char *pucSrc; 70. unsigned char *pucDest; 71. int iLineBytes; 72. 73. /* 判斷該文件是否為BMP格式 */ 74. if (IsBmp(&ptData->ptFp, strFileName)) 75. return -1; 76. 77. /* 將BMP文件映射到內存空間 */ 78. MapFile(ptData); 79. 80. 81. aFileHead = ptData->pucFileData; 82. 83. ptBITMAPFILEHEADER = (BITMAPFILEHEADER *)aFileHead; 84. ptBITMAPINFOHEADER = (BITMAPINFOHEADER *)(aFileHead + sizeof(BITMAPFILEHEADER)); 85. /* 獲取必要的圖像信息 */ 86. ptData->iWidth = ptBITMAPINFOHEADER->biWidth; 87. ptData->iHeight = ptBITMAPINFOHEADER->biHeight; 88. ptData->iBpp = ptBITMAPINFOHEADER->biBitCount; 89. iLineBytes = ptData->iWidth*ptData->iBpp/8;//一行數據的字節數 90. ptData->iBmpDataSize= ptData->iHeight * iLineBytes;//整個BMP圖像的字節數 91. /*暫時只支持24bpp格式*/ 92. if (ptData->iBpp != 24) 93. { 94. printf("iBMPBpp = %dn", ptData->iBpp); 95. printf("sizeof(BITMAPFILEHEADER) = %dn", sizeof(BITMAPFILEHEADER)); 96. return -2; 97. } 98. 99. /* 分配空間 */ 100. ptData->pucBmpData = malloc(ptData->iBmpDataSize); 101. ptData->pucRgbData = malloc(ptData->iBmpDataSize); 102. 103. if (NULL == ptData->pucBmpData||NULL == ptData->pucRgbData) 104. return -2; 105. 106. /* 從bmp文件中讀取圖像信息,24bpp的BMP圖像為BGR格式 */ 107. pucDest = ptData->pucBmpData; 108. iLineWidthAlign = (iLineBytes + 3) & ~0x3; /* 向4取整 */ 109. pucSrc = aFileHead + ptBITMAPFILEHEADER->bfOffBits; 110. 111. pucSrc = pucSrc + (ptData->iHeight - 1) * iLineWidthAlign; 112. 113. /* 對于bmp文件中的源數據,是以左下角為原點計算坐標的,因此拷貝數據時需要轉換坐標 */ 114. for (y = 0; y < ptData->iHeight; y++) 115. { 116. memcpy(pucDest, pucSrc, ptData->iWidth*3); 117. pucSrc -= iLineWidthAlign; 118. pucDest += iLineBytes; 119. } 120. 121. 122. /* 將得到的BGR數據轉化為RGB數據 */ 123. for (y = 0; y < ptData->iHeight; y++){ 124. for(x = 0;xiWidth*3;x+=3){ 125. ptData->pucRgbData[iPos++] = ptData->pucBmpData[y*ptData->iWidth*3+x+2]; 126. ptData->pucRgbData[iPos++] = ptData->pucBmpData[y*ptData->iWidth*3+x+1]; 127. ptData->pucRgbData[iPos++] = ptData->pucBmpData[y*ptData->iWidth*3+x+0]; 128. } 129. } 130. 131. return 0; 132. 133. }

2.2 JPEG圖像處理

2.2.1 JPEG文件格式和libjpeg編譯

JPEG的后綴名為.jpg的圖像文件。對于圖像內容和信息相同的JPEG文件和BMP文件,JPEG格式的文件要比BMP格式的文件小得多,這是因為JPEG文件是經過JPEG壓縮算法后得到的一種文件格式。

相對于BMP格式的文件,JPEG由于壓縮算法的關系,其文件解析較為復雜,我們可以利用Linux系統開源的優點,使用開源工具對jpeg文件進行格式的解析和轉換。

我們可以使用libjpeg庫來對jpeg文件進行格式的解析和轉換。libjpeg支持X86,ARM等架構。libjpeg是開源工具,所以可以在網上免費下載

在使用libjpeg之前,我們先要交叉編譯libjpeg的庫文件和頭文件并存到開發板的文件系統中。以下是libjpeg的編譯過程:

解壓并進入文件目錄

tar xzf libjpeg-turbo-1.2.1.tar.gz cd libjpeg-turbo-1.2.1/

交叉編譯

tar xzf libjpeg-turbo-1.2.1.tar.gz ./configure --prefix=/work/projects/libjpeg-turbo-1.2.1/tmp/ --host=arm-linux make make install

將編譯出來的頭文件和庫文件拷貝到交叉編譯器的相應目錄下

cd /work/projects/libjpeg-turbo-1.2.1/tmp/include cp * /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include cd /work/projects/libjpeg-turbo-1.2.1/tmp/lib cp *so* -d /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib

將編譯出來的頭文件和庫文件拷貝到開發板文件系統的相應目錄下

cd /work/projects/libjpeg-turbo-1.2.1/tmp/lib cp *.so* /work/nfs_root/fs_mini_mdev_new/lib/ -d

2.2.2 libjpeg接口函數的解析和使用

libjpeg的使用方法可以參考解壓包中的使用說明libjpeg.txt和例程example.c。libjpeg的使用步驟簡單總結如下:

1.分配和初始化一個jpeg_compress_struct結構體

cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo);

2.指定源文件

jpeg_stdio_src(&cinfo, infile);

參數1是步驟1中分配的jpeg_compress_struct類型的結構體

參數2是要解析的JPEG文件的文件句柄。

3.獲得jpg信息頭并設置解壓參數

jpeg_read_header(&cinfo, TRUE);

當調用完這個參數之后,我們就可以通過cinfo中的image_width,image_height等成員來獲得圖像的信息了。此外我們還可以設置cinfo中的scale_num和scale_denom等成員變量來設置解壓參數。

4.啟動解壓

jpeg_start_decompress(&cinfo);

調用這個函數后,就可以對cinfo所指定的源文件進行解壓,并將解壓后的數據存到cinfo結構體的成員變量中。

5.讀取解壓后數據

jpeg_read_scanlines(&cinfo, buffer, 1);

調用這個函數后,可以讀取RGB數據到buffer中,參數3能指定讀取多少行

6.完成讀取

jpeg_finish_decompress(&cinfo);

7.釋放jpeg_compress_struct結構體

jpeg_destroy_decompress(&cinfo);

完成讀取后釋放結構體

2.2.3 使用libjpeg把JPEG文件解析為RGB格式,在LCD上顯示

根據上節的解析,利用上述的庫函數將JPEG文件解析為RGB格式了。

代碼清單2.2 1. /********************************************************************** 2. * 函數名稱: IsJpg 3. * 功能描述:判斷是否為Jpg文件 4. * 輸入參數: ptData - 內含圖像信息 5. strFileName - 文件名 6. * 返 回 值:0 - 不是JPG格式 其他-是JPG格式 7. ***********************************************************************/ 8. static int IsJpg(PT_PictureData ptData, const char *strFileName) 9. { 10. int iRet; 11. 12. jpeg_stdio_src(&ptData->tInfo, ptData->ptFp); 13. 14. /* 用jpeg_read_header獲得jpeg信息*/ 15. iRet = jpeg_read_header(&ptData->tInfo, TRUE); 16. 17. return (iRet == JPEG_HEADER_OK); 18. } 19. 20. /********************************************************************** 21. * 函數名稱: DecodeJpg2Rgb 22. * 功能描述:把JPG文件解析為RGB888格式 23. * 輸入參數: ptData - 內含文件信息 24. * strFileName - 文件名 25. * 輸出參數:PT_PictureData->pucRgbData - 內含rgb數據 26. * 返 回 值:0 - 成功 其他-失敗 27. ***********************************************************************/ 28. static int DecodeJpg2Rgb(const char *strFileName, PT_PictureData ptData){ 29. int iRowSize; 30. unsigned char *pucbuffer; 31. unsigned char *pucHelp;//輔助拷貝變量 32. 33. /* 1.分配和初始化一個jpeg_compress_struct結構體 */ 34. ptData->tInfo.err = jpeg_std_error(&ptData->tJerr); 35. jpeg_create_decompress(&ptData->tInfo); 36. 37. 38. /* 2.指定源文件*/ 39. if ((ptData->ptFp= fopen(strFileName, "rb")) == NULL) { 40. fprintf(stderr, "can't open %sn", strFileName); 41. return -1; 42. } 43. 44. /* 3.獲得jpg信息頭并設置解壓參數并判斷是否為JPEG格式文件 */ 45. if (!IsJpg(ptData, strFileName)) { 46. printf("file is not jpg ...n"); 47. return -1; 48. } 49. 50. 51. 52. /* 默認尺寸為原尺寸 */ 53. ptData->tInfo.scale_num = 1; 54. ptData->tInfo.scale_denom = 1; 55. /* 4. 啟動解壓:jpeg_start_decompress */ 56. jpeg_start_decompress(&ptData->tInfo); 57. 58. 59. /* 解壓完成后可以通過tInfo中的成員獲得圖像的某些信息 */ 60. ptData->iWidth= ptData->tInfo.output_width; 61. ptData->iHeight = ptData->tInfo.output_height; 62. ptData->iBpp = ptData->tInfo.output_components*8; 63. /* 計算一行的數據長度 */ 64. iRowSize = ptData->iWidth * ptData->tInfo.output_components; 65. pucbuffer = malloc(iRowSize); 66. ptData->iRgbSize= iRowSize * ptData->iHeight; 67. ptData->pucRgbData = malloc(ptData->iRgbSize); 68. 69. /* pucHelp指向ptData->pucRgbData首地址 */ 70. pucHelp = ptData->pucRgbData; 71. /* 5.循環調用jpeg_read_scanlines來一行一行地獲得解壓的數據 */ 72. while (ptData->tInfo.output_scanline < ptData->tInfo.output_height) 73. { 74. /* 調用jpeg_read_scanlines得到的時候會存到pucbuffer中 */ 75. jpeg_read_scanlines(&ptData->tInfo, &pucbuffer, 1); 76. /* 將數據一行行讀到緩沖區中 */ 77. memcpy(pucHelp,pucbuffer,iRowSize); 78. pucHelp += iRowSize; 79. } 80. free(pucbuffer); 81. /* 6.完成讀取 */ 82. jpeg_finish_decompress(&ptData->tInfo); 83. /* 7.釋放jpeg_compress_struct結構體 */ 84. jpeg_destroy_decompress(&ptData->tInfo); 85. return 0; 86. }

2.3 PNG圖像處理

2.3.1 PNG文件格式和libpng編譯

跟JPEG文件格式一樣,PNG也是一種使用了算法壓縮后的圖像格式,與JPEG不同,PNG使用從LZ77派生的無損數據壓縮算法。對于PNG文件格式,也有相應的開源工具libpng。

libpng庫可從官網上下載最新的源代碼:

http://www.libpng.org/pub/png/libpng.html

在使用libpng之前,我們先要交叉編譯libpng的庫文件和頭文件并存到開發板的文件系統中。以下是libpng的編譯過程:

解壓并進入文件目錄

tar xzf libpng-1.6.37.tar.gz cd libpng-1.6.37/

交叉編譯

./configure --prefix=/work/projects/libpng-1.6.37/tmp/ --host=arm-linux make make install

將編譯出來的頭文件和庫文件拷貝到交叉編譯器的相應目錄下

cd /work/projects/libpng-1.6.37/tmp/include cp * /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include cd /work/projects/libpng-1.6.37/tmp/lib cp *so* -d /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib

將編譯出來的頭文件和庫文件拷貝到開發板文件系統的相應目錄下

cd /work/projects/libpng-1.6.37/tmp/lib cp *.so* /work/nfs_root/fs_mini_mdev_new/lib/ -d

2.3.2 libpng接口函數的解析和使用

libpng的使用方法可以參考解壓包中的使用說明libpng-manual.txt和例程example.c。libjpeg的使用步驟簡單總結如下:

分配和初始化兩個與libpng相關的結構體png_ptr,info_ptr

A. png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

參數2,3,4分別是用戶自定義的錯誤處理函數,若無,則填NULL。

B. info_ptr = png_create_info_struct(png_ptr);

設置錯誤返回點

setjmp(png_jmpbuf(png_ptr));

當出現錯誤時,libpng將會自動調用返回到這個點。在這個點我們可以進行一些清理工作。如果在調用png_create_read_struct時沒有設置自定義的錯誤處理函數,這一步是必須要做的。

指定源文件

png_init_io(png_ptr, fp);

參數1是步驟1中分配的png_ptr結構體,參數2是需要解析的PNG文件的文件句柄。

獲取PNG圖像的信息

A. 解析圖片數據信息

png_read_png(png_ptr, info_ptr, png_transforms, png_voidp_NULL);

該函數會把所有的圖片數據解碼到info_ptr數據結構中。至于轉化為什么格式,由參數png_transforms決定,它是一個整型參數,可以使用libpng庫中定義的宏進行傳參。這個參數相關的宏有很多,具體的可以參考庫中的相關文件的解析。

B.查詢圖像信息

此外,我們還可以通過png_get_image_width,png_get_image_height,png_get_color_type等函數獲得png圖像的寬度,高度,顏色類型等信息,更多的圖像信息獲取函數可以在文件pngget.c中找到。

將info_ptr中的圖像數據讀取出來

有兩種讀取PNG圖像信息的方法:

A. 一次性把所有的數據讀入內存

png_read_image(png_ptr, row_pointers);

參數1是步驟1中分配的png_ptr,參數2是存放圖片數據的指針。

B. 也可以逐行讀取

row_pointers = png_get_rows(png_ptr, info_ptr);

參數1和參數2分別是步驟1中分配的png_ptr, info_ptr,返回值是每行數據的首地址。

參數1是步驟1中分配的png_ptr,參數2是存放圖片數據的指針。

銷毀內存

png_destroy_read_struct(&png_ptr, &info_ptr, 0);

2.3.3 使用libpng把png文件轉為rgb格式,在LCD上顯示

代碼清單2.3 1. /********************************************************************** 2. * 函數名稱: IsnotPng 3. * 功能描述:判斷是否為PNG文件 4. * 輸入參數: ppFp - 文件句柄指針 5. strFileName - 文件名 6. * 返 回 值:0 - 是PNG格式 其他-不是PNG格式 7. ***********************************************************************/ 8. int IsnotPng(FILE **ppFp, const char *strFileName) 9. { 10. char strCheckHeader[8]; 11. *ppFp= fopen(strFileName, "rb"); 12. if (*ppFp== NULL) { 13. return -1; 14. } 15. /* 讀取PNG文件前8個字節,使用庫函數png_sig_cmp即可判斷是否為PNG格式 */ 16. if (fread(strCheckHeader, 1, 8, *ppFp) != 8) 17. return -1; 18. return png_sig_cmp(strCheckHeader, 0, 8); 19. 20. } 21. 22. /********************************************************************** 23. * 函數名稱: DecodePng2Rgb 24. * 功能描述:把PNG文件解析為RGB888格式 25. * 輸入參數: ptData - 內含文件信息 26. * strFileName - 文件名 27. * 輸出參數:PT_PictureData->pucRgbData - 內含rgb數據 28. * 返 回 值:0 - 成功 其他-失敗 29. ***********************************************************************/ 30. static int DecodePng2Rgb(const char *strFileName, PT_PictureData ptData) 31. { 32. int i, j; 33. int iPos = 0; 34. png_bytepp pucPngData; 35. /* 0.判斷該文件是否為PNG格式 */ 36. if (IsnotPng(&ptData->ptFp, strFileName)) { 37. printf("file is not png ...n"); 38. return -1; 39. } 40. 41. /* 1.分配和初始化兩個與libpng相關的結構體png_ptr,info_ptr */ 42. ptData->ptPngStrPoint = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 43. ptData->ptPngInfoPoint= png_create_info_struct(ptData->ptPngStrPoint); 44. 45. /* 2.設置錯誤的返回點 */ 46. setjmp(png_jmpbuf(ptData->ptPngStrPoint)); 47. rewind(ptData->ptFp); //等價fseek(fp, 0, SEEK_SET); 48. 49. /* 3.指定源文件 */ 50. png_init_io(ptData->ptPngStrPoint, ptData->ptFp); 51. 52. /* 4.獲取PNG圖像數據信息和通道數,寬度,高度等 53. * 使用PNG_TRANSFORM_EXPAND宏做參數的作用是根據通道數的不同, 54. * 將PNG圖像轉換為BGR888或ABGR8888格式*/ 55. png_read_png(ptData->ptPngStrPoint, ptData->ptPngInfoPoint, PNG_TRANSFORM_EXPAND, 0); 56. ptData->iChannels = png_get_channels(ptData->ptPngStrPoint, ptData->ptPngInfoPoint); 57. ptData->iWidth = png_get_image_width(ptData->ptPngStrPoint, ptData->ptPngInfoPoint); 58. ptData->iHeight = png_get_image_height(ptData->ptPngStrPoint, ptData->ptPngInfoPoint); 59. 60. 61. /* 5.將info_ptr中的圖像數據讀取出來 */ 62. pucPngData = png_get_rows(ptData->ptPngStrPoint, ptData->ptPngInfoPoint); //也可以分別每一行獲取png_get_rowbytes(); 63. if (ptData->iChannels == 4) { //判斷是24位還是32位 64. ptData->iRawSize= ptData->iWidth * ptData->iHeight*4; //申請內存先計算空間 65. ptData->pucRawData= (unsigned char*)malloc(ptData->iRawSize); 66. if (NULL == ptData->pucRawData) { 67. printf("malloc rgba faile ...n"); 68. png_destroy_read_struct(&ptData->ptPngStrPoint, &ptData->ptPngInfoPoint, 0); 69. fclose(ptData->ptFp); 70. return -1; 71. } 72. /* 從pucPngData里讀出實際的RGBA數據出來 73. * 源數據為ABGR格式*/ 74. for (i = 0; i < ptData->iHeight; i++) 75. for (j = 0; j < ptData->iWidth * 4; j += 4) { 76. ptData->pucRawData[iPos++] = pucPngData[i][j + 3]; 77. ptData->pucRawData[iPos++] = pucPngData[i][j + 2]; 78. ptData->pucRawData[iPos++] = pucPngData[i][j + 1]; 79. ptData->pucRawData[iPos++] = pucPngData[i][j + 0]; 80. } 81. 82. /* 將得到的RGBA轉換為RGB888格式 */ 83. if(RgbaToRgb(ptData)!=0) 84. return -1; 85. 86. } 87. else if (ptData->iChannels == 3 ) { //判斷顏色深度是24位還是32位 88. ptData->iRgbSize= ptData->iWidth * ptData->iHeight*3; //申請內存先計算空間 89. ptData->pucRgbData = (unsigned char*)malloc(ptData->iRgbSize); 90. if (NULL == ptData->pucRgbData) { 91. printf("malloc rgba faile ...n"); 92. png_destroy_read_struct(&ptData->ptPngStrPoint, &ptData->ptPngInfoPoint, 0); 93. fclose(ptData->ptFp); 94. return -1; 95. } 96. /* 從pucPngData里讀出實際的RGB數據 97. * 源數據為BGR格式*/ 98. for (i = 0; i < ptData->iHeight; i ++) { 99. for (j = 0; j < ptData->iWidth*3; j += 3) { 100. ptData->pucRgbData[iPos++] = pucPngData[i][j+2]; 101. ptData->pucRgbData[iPos++] = pucPngData[i][j+1]; 102. ptData->pucRgbData[iPos++] = pucPngData[i][j+0]; 103. } 104. } 105. ptData->iBpp = 24;//轉化之后的格式為RGB888格式 106. } 107. else return -1; 108. 109. 110. /* 6:銷毀內存 */ 111. png_destroy_read_struct(&ptData->ptPngStrPoint, &ptData->ptPngInfoPoint, 0); 112. fclose(ptData->ptFp); 113. 114. 115. return 0; 116. }

2.4 圖像調整

2.4.1 圖像的縮放

2.4.1.1 圖像縮放算法淺析

圖像縮放算法有很多種,這里參考網友"lantianyu520"所著的"圖像縮放算法"。

原理淺析

要理解這個圖像縮放算法的原理,最重要的是需要理解:對于圖像上的每一個像素點,它縮放前后,相對于整個圖像的比例應該是一樣的。

比如:

以一個長度和寬度分別為200,100的長方形為例,將其放大兩倍,那么縮放后的長度和寬度為400,200。

為方便理解,我們建立一個笛卡爾坐標系,把這個長方形左下角的頂點放到坐標(0,0)位置,四個點的坐標分別為:(0,0),(0,100),(200,0),(200,100)。

假設此時對長方形中的坐標點(40,50),它的x坐標相對于長的比值是40/200=0.2,y坐標相對于寬的比值是50/100=0.5,那么該點的變換后的坐標Dx,Dy則應滿足:Dx/400 = 5;Dy/200 = 0.5,這樣,縮放后的坐標就可以算出來了。

根據上面的分析,設縮放前的像素點坐標為(Sx,Sy),對應的縮放后的像素點坐標為(Dx,Dy),縮放前的圖像長寬分別為Sw,Sh,縮放后的圖像長寬分別為Dw,Dh,則有:

Sx/Dx = Sw/Dw,Sy/Dy = Sh/Dh

故有Sx = Dx * Sw/Dw,Sy = Dy * Sh/Dh,

2.4.1.2源碼編寫:圖像縮放算法

有了這個上面兩條等式后,圖像縮放算法的代碼就好理解了。

下面的函數實現了基于上述原理實現的圖像縮放算法:

代碼清單2.4 1. /********************************************************************** 2. * 函數名稱: PicZoom 3. * 功能描述: 近鄰取樣插值方法縮放圖片 4. * 注意該函數會分配內存來存放縮放后的圖片,用完后要用free函數釋放掉 5. * "近鄰取樣插值"的原理請參考網友"lantianyu520"所著的"圖像縮放算法" 6. * 輸入參數: ptPicData - 內含縮放前后的圖像數據 7. * fSize - 縮放倍數 8. * 輸出參數: ptPicData->pucZoomData,內含縮放后的數據 9. * 返 回 值: 0 - 成功, 其他值 - 失敗 10. ***********************************************************************/ 11. int PicZoom(PT_PictureData ptPicData,float fSize) 12. { 13. ptPicData->iZoomWidth = ptPicData->iWidth * fSize; 14. ptPicData->iZoomHeight= ptPicData->iHeight* fSize; 15. unsigned long* pdwSrcXTable; 16. unsigned long x; 17. unsigned long y; 18. unsigned long dwSrcY; 19. unsigned char *pucDest; 20. unsigned char *pucSrc; 21. unsigned long dwPixelBytes = ptPicData->iBpp/8; 22. ptPicData->pucZoomData= malloc(sizeof(unsigned char) * ptPicData->iZoomWidth*ptPicData->iZoomHeight*ptPicData->iBpp/8); 23. pdwSrcXTable = malloc(sizeof(unsigned long) * ptPicData->iZoomWidth); 24. if (NULL == pdwSrcXTable){ 25. printf("malloc error!n"); 26. return -1; 27. } 28. 29. /* 這幾個for循環的本質是Sx = Dx * Sw/Dw,Sy = Dy * Sh/Dh*/ 30. for (x = 0; x < ptPicData->iZoomWidth; x++){//生成表 pdwSrcXTable 31. /* 第一個for循環對應x方向的坐標 32. * pdwSrcXTable[x] 對應Sx, 33. * x 對應Dx, 34. * ptPicData->iWidth 對應Sw 35. * ptPicData->iZoomWidth 對應 Dw*/ 36. pdwSrcXTable[x]=(x*ptPicData->iWidth/ptPicData->iZoomWidth); 37. } 38. 39. for (y = 0; y < ptPicData->iZoomHeight; y++){ 40. /* 第2個循環對應y方向的坐標 41. * dwSrcY 對應Sy, 42. * y 對應Dy, 43. * ptPicData->iHeight 對應Sh 44. * ptPicData->iZoomHeight 對應 Dh*/ 45. dwSrcY = (y * ptPicData->iHeight / ptPicData->iZoomHeight); 46. /* 根據這些可算得各像素點的RGB數據存放的地址 */ 47. pucDest = ptPicData->pucZoomData + y*ptPicData->iZoomWidth*3; 48. pucSrc = ptPicData->pucRgbData + dwSrcY*ptPicData->iWidth*3; 49. 50. /* 最后拷貝數據 */ 51. for (x = 0; x iZoomWidth; x++){ 52. memcpy(pucDest+x*dwPixelBytes, pucSrc+pdwSrcXTable[x]*dwPixelBytes, dwPixelBytes); 53. } 54. } 55. 56. free(pdwSrcXTable); 57. return 0; 58. }

2.4.2 圖像的旋轉

2.4.2.1 圖像旋轉算法淺析

這里的圖像旋轉算法原理參考網友"落葉的思維"所著的"圖像旋轉算法與實現"

原理淺析

這個旋轉算法的原理的關鍵點有兩個:

原圖像是以圖像的左下角為原點建立笛卡爾坐標系的,而旋轉一般是以圖像的中心作為旋轉點旋轉的。

因此為了便于轉換,我們先約定兩個坐標系,一個是以圖像左下角為原點建立的坐標系,稱為坐標系A,這也是原圖像的坐標系。一個是以圖像中心為原點建立的坐標系,稱為坐標系B。

由此,可以知道這個旋轉算法的步驟:先將坐標系A下的坐標轉換為坐標系B下的坐標,然后在坐標系B下進行圖像的旋轉。

在坐標系B下,我們假設點(x0,y0)距離原點的距離為r,點與原點之間的連線與x軸的夾角為b,旋轉的角度為a,旋轉后的點為(x1,y1), 如下圖所示。

pYYBAGGzIMaAU--RAAAWMozMNJQ357.png

那么有以下結論:

x0=rcosb;y0=rsinb

x1 = rcos(b-a) = rcosbcosa+rsinbsina=x0cosa+y0sina;

y1=rsin(b-a)=rsinbcosa-rcosbsina=-x0sina+y0cosa;

最后,由于我們顯示圖像的RGB數據還是要在坐標系A下獲取的,我們最后只需要將坐標系B下的x1,y1轉換回坐標系A下的坐標就可以了。

旋轉后的圖像的長和寬會發生變化,因此要計算新圖像的長和寬。

由幾何關系可知,新圖像的長和寬分別是旋轉后,對角坐標相見后的最大值

2.4.2.2 源碼編寫:圖像旋轉算法

代碼清單2.5 1. #define PI 3.1415926535 2. //角度到弧度轉化 3. #define RADIAN(angle) ((angle)*PI/180.0) 4. 5. 6. 7. 8. 9. typedef struct ConcernCoor { 10. int iLTx;// left top x 11. int iLTy;//left top y 12. int iLBx;//left bottom x 13. int iLBy;//left bottom y 14. int iRTx;//right top x 15. int iRTy;//right top y 16. int iRBx;// right bottom x 17. int iRBy;// right bottom y 18. }T_ConcernCoor, *PT_ConcernCoor; 19. 20. 21. /********************************************************************** 22. * 函數名稱: max 23. * 功能描述:比較兩個參數,返回較大值 24. * 輸入參數:x,y均為int型 25. * 輸出參數: 無 26. * 返 回 值: x,y中的較大值 27. ***********************************************************************/ 28. static int max(int x,int y){ 29. return x>y?x:y; 30. } 31. /********************************************************************** 32. * 函數名稱: PicRotate 33. * 功能描述: 旋轉圖片 34. * 注意該函數會分配內存來存放縮放后的圖片,用完后要用free函數釋放掉 35. * 參考網友"落葉的思維"所著的"圖像旋轉算法與實現" 36. * 輸入參數: ptPicData - 內含圖片的象素數據 37. * fAngle - 旋轉角度,0<=angle<=360 38. * 輸出參數: ptPicData->pucRotateData,內含旋轉后的rgb數據 39. * 返 回 值: 0 - 成功, 其他值 - 失敗 40. ***********************************************************************/ 41. int PicRotate(PT_PictureData ptPicData,float fAngle) 42. { 43. int i ,j; 44. T_ConcernCoor tConCor,tRonCor; 45. //原圖像每一行去除偏移量的字節數 46. //int iSrcLineSize = bitCount * srcW / 8; 47. int iSrcLineSize = ptPicData->iBpp* ptPicData->iZoomWidth / 8; 48. int iDesLineSize; 49. int iX;//旋轉后的x坐標 50. int iY; //旋轉后的y坐標 51. 52. /* 將坐標系A下的坐標轉換為坐標系B下的坐標, 53. * 用于計算旋轉后的圖像的寬和高 54. * tConCor用于存放坐標系B下旋轉前的坐標 55. * tRonCor用于存放坐標系B下旋轉后的坐標*/ 56. tConCor.iLTx = -ptPicData->iZoomWidth/2; tConCor.iLTy = ptPicData->iZoomHeight/2; 57. tConCor.iRTx = ptPicData->iZoomWidth/2; tConCor.iRTy = ptPicData->iZoomHeight/2; 58. tConCor.iLBx = -ptPicData->iZoomWidth/2;tConCor.iLBy = -ptPicData->iZoomHeight/2; 59. tConCor.iRBx = ptPicData->iZoomWidth/2;tConCor.iRBy = -ptPicData->iZoomHeight/2; 60. 61. 62. /* 計算坐標系B下旋轉后的坐標 */ 63. double sina = sin(RADIAN(fAngle)); 64. double cosa = cos(RADIAN(fAngle)); 65. tRonCor.iLTx =tConCor.iLTx * cosa + tConCor.iLTy * sina; 66. tRonCor.iLTy = -tConCor.iLTx * sina + tConCor.iLTy * cosa; 67. tRonCor.iRTx =tConCor.iRTx * cosa + tConCor.iRTy * sina; 68. tRonCor.iRTy = -tConCor.iRTx * sina + tConCor.iRTy * cosa; 69. tRonCor.iLBx = tConCor.iLBx * cosa + tConCor.iLBy * sina; 70. tRonCor.iLBy = -tConCor.iLBx * sina + tConCor.iLBy * cosa; 71. tRonCor.iRBx = tConCor.iRBx * cosa + tConCor.iRBy * sina; 72. tRonCor.iRBy = -tConCor.iRBx * sina + tConCor.iRBy * cosa; 73. 74. 75. /* 計算旋轉后圖像寬和高 */ 76. ptPicData->iRotateWidth = max(abs(tRonCor.iRBx - tRonCor.iLTx),abs(tRonCor.iRTx - tRonCor.iLBx)); 77. ptPicData->iRotateHeight = max(abs(tRonCor.iRBy - tRonCor.iLTy),abs(tRonCor.iRTy - tRonCor.iLBy)); 78. 79. /* 像素信息要保證3字節對齊,否則數據有可能出錯*/ 80. iDesLineSize = ((ptPicData->iRotateWidth* ptPicData->iBpp+ 23) / 24) * 3 ; 81. /* 分配旋轉后的空間,注意這里要用旋轉后的寬和高 */ 82. ptPicData->pucRotateData = malloc(iDesLineSize * ptPicData->iRotateHeight); 83. if(NULL == ptPicData->pucRotateData){ 84. printf("malloc errorn"); 85. return -1; 86. } 87. 88. /* 通過新圖像的坐標,計算對應的原圖像的坐標* 89. * i,j坐標就是對應的坐標系B下的x1,y1*/ 90. for (i = 0; i < ptPicData->iRotateHeight; i++){ 91. for (j = 0; j < ptPicData->iRotateWidth; j++){ 92. /* 坐標系B下的x,y1坐標,經過逆運算轉換得到iX,iY,這兩個值對應x0,y0 */ 93. iX = (j - ptPicData->iRotateWidth / 2)*cos(RADIAN(360 - fAngle)) + (-i + ptPicData->iRotateHeight / 2)*sin(RADIAN(360 - fAngle)); 94. iY = -(j - ptPicData->iRotateWidth / 2)*sin(RADIAN(360 - fAngle)) + (-i + ptPicData->iRotateHeight / 2)*cos(RADIAN(360 - fAngle)); 95. /*如果這個坐標不在原圖像內,則不賦值*/ 96. if (iX > ptPicData->iZoomWidth / 2 || iX < -ptPicData->iZoomWidth / 2 || iY > ptPicData->iZoomHeight / 2 || iY < -ptPicData->iZoomHeight / 2){ 97. continue; 98. } 99. /* 再將坐標系B下的x0,y0坐標,轉換為坐標系A下的坐標 */ 100. int iXN = iX + ptPicData->iZoomWidth / 2; 101. int iYN = abs(iY - ptPicData->iZoomHeight / 2); 102. /* 值拷貝*/ 103. memcpy(&ptPicData->pucRotateData[i * iDesLineSize + j * 3],&ptPicData->pucZoomData[iYN * iSrcLineSize + iXN * 3],3); 104. } 105. } 106. return 0; 107. } 審核編輯 黃昊宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11345

    瀏覽量

    210403
收藏 人收藏

    評論

    相關推薦

    【ALIENTEK 戰艦STM32開發板】第二章 實驗平臺硬件資源詳解

    本帖最后由 正點原子 于 2013-1-10 15:40 編輯   第二章 實驗平臺硬件資源詳解 本章,我們將節將向大家詳細介紹ALIENTEK戰艦STM32開發板各部分的硬件原理圖,讓大家
    發表于 01-10 15:37

    高頻電子線路第二章答案

    高頻電子線路第二章答案.rar
    發表于 06-05 10:34 ?3次下載

    javascript語言精髓與編程實踐(第二章)介紹

    javascript語言精髓與編程實踐(第二章)
    發表于 10-30 10:07 ?0次下載

    第二章__電網的電流保護

    繼電保護的第二章,關于電流的保護措施,與保護所涉及的方法。
    發表于 03-14 14:42 ?1次下載

    大學電路基礎第二章

    大學電路基礎第二章。
    發表于 03-14 11:32 ?0次下載

    第二章 模擬電路常用元器件

    模擬電子技術第二章課件,主要介紹電路中常用的元器件。
    發表于 05-10 16:31 ?8次下載

    第二章 邏輯門電路

    數字電子技術,第二章,邏輯門電路,講的比較詳細,值得一看哦,西安電子科技大學出版社教材
    發表于 05-20 14:47 ?0次下載

    第二章 數制運算

    第二章 數制運算
    發表于 12-16 15:36 ?0次下載

    數字信號處理課件--第二章6離散系統的系統函數、系統的頻率響應

    數字信號處理課件--第二章6離散系統的系統函數、系統的頻率響應
    發表于 12-28 14:23 ?0次下載

    《測控電路》習題完整參考答案(第二章

    《測控電路》習題完整參考答案(第二章
    發表于 02-07 15:17 ?0次下載

    第二章 Android系統與嵌入式開發

    第二章Android系統與嵌入式開發第二章首先要先了解Android和嵌入式Lnux系統有什么區別和聯系,嵌入式Linux系統是在嵌入式設備中運行L
    發表于 11-02 20:51 ?13次下載
    <b class='flag-5'>第二章</b> Android系統與嵌入式<b class='flag-5'>開發</b>

    慕課嵌入式開發及應用(第二章.分析一個匯編實例)

    慕課蘇州大學.嵌入式開發及應用.第二章.入門與軟件框架.分析一個匯編實例0 目錄2 入門與軟件框架2.4 分析一個匯編實例2.4.1 課堂重點2.4.2 測試與作業3 下一0 目錄2 入門與軟件
    發表于 11-03 11:36 ?11次下載
    慕課嵌入式<b class='flag-5'>開發</b>及應用(<b class='flag-5'>第二章</b>.分析一個匯編實例)

    慕課嵌入式開發及應用(第二章.UART驅動構件的設計方法)

    慕課蘇州大學.嵌入式開發及應用.第二章.入門與軟件框架.UART驅動構件的設計方法0 目錄2 入門與軟件框架2.1 UART驅動構件的設計方法2.1.1 課堂重點2.1.2 測試與作業3 下一0
    發表于 11-03 13:51 ?5次下載
    慕課嵌入式<b class='flag-5'>開發</b>及應用(<b class='flag-5'>第二章</b>.UART驅動構件的設計方法)

    FPGA基礎知識----第二章 FPGA 開發流程

    第二章 FPGA 開發流程FPGA 的設計流程就是利用 EDA 開發軟件和編程工具對 FPGA 芯片進行開發的過程。原理圖和HDL(Hardware description langu
    發表于 12-29 19:40 ?9次下載
    FPGA基礎知識----<b class='flag-5'>第二章</b> FPGA <b class='flag-5'>開發</b>流程

    電工學教程第二章電路的分析方法

    電工學教程第二章電路的分析方法
    發表于 02-07 14:38 ?0次下載
    百家乐官网tt赌场娱乐网规则| 佛山市| 台州星空棋牌下载| 大发888手机版下载安| 大发888m摩卡游戏| e世博娱乐| 莫力| 百家乐官网翻天qvod粤语| 视频百家乐官网平台出租| 百家乐官网赌博规律| 百家乐官网明灯| 巴特百家乐官网的玩法技巧和规则 | 百家乐官网五湖四海娱乐| 布加迪百家乐官网的玩法技巧和规则 | 风水罗盘里的24山| 百家乐庄闲筹码| 国美百家乐的玩法技巧和规则| 大发888 df登录| 爱博| 百家乐官网视频游戏道具| 金百家乐官网网站| 金都百家乐官网的玩法技巧和规则| 百家乐现金平台排名| 龙博百家乐的玩法技巧和规则| 大发888熊之舞怎么玩| 安化县| 百家乐官网是怎样算牌| 马牌百家乐现金网| 百樂坊百家乐的玩法技巧和规则 | 百家乐官网新规则| 百家乐好多假网站| 百家乐高手论| 百家乐技巧| 百家乐官网游戏厅| 澳门百家乐庄闲的玩法| 大发888娱乐城客户端| 百家乐官网论坛白菜| 深圳百家乐官网的玩法技巧和规则| 澳门百家乐走势图怎么看| 大发888娱乐城m88| 澳门百家乐官网娱乐城送体验金|