為了便於學習圖像處理並研究圖像算法,
俺寫了一個適合初學者學習的小小框架。
麻雀雖小五臟俱全。
采用Decoder:stb_image
https://github.com/nothings/stb/blob/master/stb_image.h
采用Encoder:tiny_jpeg
https://github.com/serge-rgb/TinyJPEG/blob/master/tiny_jpeg.h
stb_image.h用於解析圖片格式:
JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
tiny_jpeg.h用於保存JPG格式。
附帶處理耗時計算,示例演示了一個簡單的反色處理算法,並簡單注釋了一下部分邏輯。
完整代碼:
//如果是Windows的話,調用系統API ShellExecuteA打開圖片 #if defined(_MSC_VER) #define _CRT_SECURE_NO_WARNINGS #include <windows.h> #define USE_SHELL_OPEN #endif #define STB_IMAGE_STATIC #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" //ref:https://github.com/nothings/stb/blob/master/stb_image.h #define TJE_IMPLEMENTATION #include "tiny_jpeg.h" //ref:https://github.com/serge-rgb/TinyJPEG/blob/master/tiny_jpeg.h #include <math.h> #include <io.h> #include <iostream> #include <string> #include <chrono> //計時 auto const epoch = std::chrono::steady_clock::now(); static double now() { return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - epoch).count() / 1000.0; }; template <typename FN> static double bench(const FN &fn) { auto took = -now(); return (fn(), took + now()); } //存儲當前傳入文件位置的變量 std::string m_curFilePath; //加載圖片 void loadImage(const char *filename, unsigned char *&Output, int &Width, int &Height, int &Channels) { Output = stbi_load(filename, &Width, &Height, &Channels, 0); } //保存圖片 void saveImage(const char *filename, int Width, int Height, int Channels, unsigned char *Output, bool open = true) { std::string saveFile = m_curFilePath; saveFile += filename; //保存為jpg if (!tje_encode_to_file(saveFile.c_str(), Width, Height, Channels, Output)) { fprintf(stderr, "寫入 JPEG 文件失敗.\n"); return; } #ifdef USE_SHELL_OPEN if (open) ShellExecuteA(NULL, "open", saveFile.c_str(), NULL, NULL, SW_SHOW); #else //其他平台暫不實現 #endif } //取當前傳入的文件位置 void getCurrentFilePath(const char *filePath, std::string &curFilePath) { char drive[_MAX_DRIVE]; char dir[_MAX_DIR]; char fname[_MAX_FNAME]; char ext[_MAX_EXT]; curFilePath.clear(); _splitpath_s(filePath, drive, dir, fname, ext); curFilePath += drive; curFilePath += dir; curFilePath += fname; curFilePath += "_"; } //算法處理,這里以一個反色作為例子 void processImage(unsigned char *Input, unsigned char *Output, unsigned int Width, unsigned int Height, unsigned int Channels) { int Stride = Width * Channels; if (Channels == 1) { for (unsigned int Y = 0; Y < Height; Y++) { unsigned char *scanLineOut = Output + (Y * Stride); unsigned char *scanLineIn = Input + (Y * Stride); for (unsigned int X = 0; X < Width; X++) { scanLineOut[0] = 255 - scanLineIn[0]; scanLineIn++; scanLineOut++; } } } else if (Channels == 3 || Channels == 4) { for (unsigned int Y = 0; Y < Height; Y++) { unsigned char *scanLineOut = Output + (Y * Stride); unsigned char *scanLineIn = Input + (Y * Stride); for (unsigned int X = 0; X < Width; X++) { scanLineOut[0] = 255 - scanLineIn[0]; scanLineOut[1] = 255 - scanLineIn[1]; scanLineOut[2] = 255 - scanLineIn[2]; //通道數為4時,不處理A通道反色(scanLineOut[3] = 255- scanLineIn[3]; scanLineIn += Channels; scanLineOut += Channels; } } } } int main(int argc, char **argv) { std::cout << "Image Processing " << std::endl; std::cout << "博客:http://cpuimage.cnblogs.com/" << std::endl; std::cout << "支持解析如下圖片格式:" << std::endl; std::cout << "JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC" << std::endl; //檢查參數是否正確 if (argc < 2) { std::cout << "參數錯誤。" << std::endl; std::cout << "請拖放文件到可執行文件上,或使用命令行:imageProc.exe 圖片" << std::endl; std::cout << "例如: imageProc.exe d:\\image.jpg" << std::endl; return 0; } std::string szfile = argv[1]; //檢查輸入的文件是否存在 if (_access(szfile.c_str(), 0) == -1) { std::cout << "輸入的文件不存在,參數錯誤!" << std::endl; } getCurrentFilePath(szfile.c_str(), m_curFilePath); int Width = 0; //圖片寬度 int Height = 0; //圖片高度 int Channels = 0; //圖片通道數 unsigned char *inputImage = NULL; //輸入圖片指針 double nLoadTime = bench([&] { //加載圖片 loadImage(szfile.c_str(), inputImage, Width, Height, Channels); }); std::cout << " 加載耗時: " << int(nLoadTime * 1000) << " 毫秒" << std::endl; if ((Channels != 0) && (Width != 0) && (Height != 0)) { //分配與載入同等內存用於處理后輸出結果 unsigned char *outputImg = (unsigned char *)stbi__malloc(Width * Channels * Height * sizeof(unsigned char)); if (inputImage) { //如果圖片加載成功,則將內容復制給輸出內存,方便處理 memcpy(outputImg, inputImage, Width * Channels * Height); } else { std::cout << " 加載文件: \n" << szfile.c_str() << " 失敗!" << std::endl; } double nProcessTime = bench([&] { //處理算法 processImage(inputImage, outputImg, Width, Height, Channels); }); std::cout << " 處理耗時: " << int(nProcessTime * 1000) << " 毫秒" << std::endl; //保存處理后的圖片 double nSaveTime = bench([&] { saveImage("_done.jpg", Width, Height, Channels, outputImg); }); std::cout << " 保存耗時: " << int(nSaveTime * 1000) << " 毫秒" << std::endl; //釋放占用的內存 if (outputImg) { stbi_image_free(outputImg); outputImg = NULL; } if (inputImage) { stbi_image_free(inputImage); inputImage = NULL; } } else { std::cout << " 加載文件: \n" << szfile.c_str() << " 失敗!" << std::endl; } getchar(); std::cout << "按任意鍵退出程序 \n" << std::endl; return EXIT_SUCCESS; }
示例具體流程為:
加載圖片(拖放文件到可執行文件上)->算法處理->保存圖片->打開保存圖片(僅Windows)
並對 加載,處理,保存 這三個環節都進行了耗時計算並輸出。
若有其他相關問題或者需求也可以郵件聯系俺探討。
郵箱地址是:
gaozhihan@vip.qq.com
若此博文能幫到您,歡迎掃碼小額贊助。
微信:
支付寶: