├── .gitignore ├── CMakeLists.txt ├── README.md ├── bin ├── IMG.DNG └── main.exe ├── include ├── common.h └── module.h └── src ├── CMakeLists.txt ├── common.cpp ├── main.cpp └── module.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.png 3 | .vs/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.0) 2 | project(OPENISP_CPP VERSION 0.1.0 LANGUAGES C CXX) 3 | 4 | add_subdirectory(src) 5 | 6 | set(CPACK_PROJECT_NAME ${PROJECT_NAME}) 7 | set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) 8 | include(CPack) 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # openISP_cpp 2 | Open Image Signal Processor 3 | You can found the description in [Practice record](https://jay1060950003.github.io/2023/04/07/isp_pipeline%E7%BB%83%E4%B9%A0%E5%AE%9E%E5%BD%95/) 4 | 5 | ## Introduction 6 | 7 | As the name implies, openISP_cpp is a C++ implementation of the [openISP](https://github.com/cruxopen/openISP) project. 8 | 9 | Here is the running time in my i5-12500H machine with the 4032x3024 input Bayer array: 10 | 11 | |Module |openISP(1920x1080) |cpp(4032x3024)| 12 | |:-----------------:|:------:|:----------:| 13 | |DPC |20.57s |17.8s | 14 | |BLC |11.75s |0.38s | 15 | |AAF |16.87s |0.52s | 16 | |AWB |7.54s |0.41s | 17 | |CNF |73.99s |----- | 18 | |BNF |------ |1.16s | 19 | |CFA |40.71s |0.05s | 20 | |CCM |56.85s |0.93s | 21 | |GAC |25.71s |0.61s | 22 | |CSC |60.32s |0.34s | 23 | |NLM |1600.95s|12.55s | 24 | |BNF |801.24s |8.18s | 25 | |EEH |68.60s |1.65s | 26 | |FCS |25.07s |----- | 27 | |HSC |56.34s |----- | 28 | |End-to-end pipeline|2894.41s|47.99s | 29 | 30 | ## Usage 31 | 32 | 1. Clone this repo 33 | 2. Compile (I use `cmake` to compile the code with `MSYS2`, `MinGW64`, `LibRaw` and `OpenCV`) 34 | 3. Adjust the path and parameters of the code, than you can do the pipeline 35 | 36 | **Note:** 37 | - I used my `iPhone 11` to get the `raw dng file`. You can try to get the raw data in dng format with your own device. It is very simple on Android phones. On an iPhone you may need `Pro device `or apps like `Procam` or `Halide` 38 | - To process the dng file, libraw is used to get the data 39 | 40 | ## Thanks to 41 | 42 | [libraw](https://github.com/LibRaw/LibRaw): Library for reading and processing of RAW digicam images 43 | 44 | [openISP](https://github.com/cruxopen/openISP): Open Image Signal Processor (openISP) 45 | 46 | [fast-openISP](https://github.com/QiuJueqin/fast-openISP): Fast Open Image Signal Processor (fast-openISP) -------------------------------------------------------------------------------- /bin/IMG.DNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jay1060950003/openISP_cpp/fbe1cad30146b4e9df3e596c8d3b6285066ba7ad/bin/IMG.DNG -------------------------------------------------------------------------------- /bin/main.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jay1060950003/openISP_cpp/fbe1cad30146b4e9df3e596c8d3b6285066ba7ad/bin/main.exe -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef _COMMON_H_ 3 | #define _COMMON_H_ 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | int median(int, int, int); 11 | cv::Mat padding(cv::Mat&, uchar*); 12 | std::vector split_bayer(cv::Mat&, std::string&); 13 | cv::Mat reconstruct_bayer(std::vector&, std::string&); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /include/module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef _MODULE_H_ 3 | #define _MODULE_H_ 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "common.h" 13 | 14 | const float sdr_max_value = 255.0; 15 | 16 | void dpc(cv::Mat&, uchar); 17 | void blc(cv::Mat&, std::string, float, float); 18 | void aaf(cv::Mat&, cv::Mat&); 19 | void wbgc(cv::Mat&, std::string, std::vector, ushort); 20 | void bnr(cv::Mat&, std::string, uchar); 21 | void cfa(cv::Mat&, std::string); 22 | void ccm(cv::Mat&, cv::Mat&); 23 | void gc(cv::Mat&, float, ushort); 24 | void csc(cv::Mat&, cv::Mat&, cv::Mat&, cv::Mat&, float[3][4]); 25 | void nlm(cv::Mat&, uchar, uchar, uchar, uchar); 26 | void bnf(cv::Mat&, uchar, uchar, ushort clip); 27 | void ee(cv::Mat&, cv::Mat&, char[3][5], ushort); 28 | void bcc(cv::Mat&, uchar, uchar, ushort); 29 | void yuv2rgb(cv::Mat&, cv::Mat&, cv::Mat&, cv::Mat&); 30 | #endif -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | aux_source_directory (. SRC_LIST) 2 | 3 | set(LibRaw_INCLUDE_DIRS C:/Users/jay10/msys64/mingw64/include/libraw) 4 | set(LibRaw_LIBS_DIRS C:/Users/jay10/msys64/mingw64/lib) 5 | set(LibRaw_LIBS ${LibRaw_LIBS_DIRS}/libraw_r.dll.a) 6 | find_package( OpenCV REQUIRED ) 7 | 8 | 9 | include_directories( ${OpenCV_INCLUDE_DIRS} ) 10 | include_directories( ${LibRaw_INCLUDE_DIRS}) 11 | 12 | include_directories(../include) 13 | 14 | add_executable (main ${SRC_LIST}) 15 | target_link_libraries(main ${OpenCV_LIBS} ) 16 | target_link_libraries(main ${LibRaw_LIBS}) 17 | 18 | set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) -------------------------------------------------------------------------------- /src/common.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | int median(int a, int b, int c) { 4 | return a >= b ? (b >= c ? b : (a >= c ? c : a)) : (a >= c ? a : (b >= c ? c : b)); 5 | } 6 | 7 | cv::Mat padding(cv::Mat& img, uchar* pads) 8 | { 9 | cv::Mat img_pad = cv::Mat(cv::Size(img.cols + pads[2] + pads[3], img.rows + pads[0] + pads[1]), img.type()); 10 | cv::copyMakeBorder(img, img_pad, pads[0], pads[1], pads[2], pads[3], cv::BORDER_REFLECT); 11 | return img_pad; 12 | } 13 | 14 | std::vector split_bayer(cv::Mat& bayer_array, std::string& bayer_pattern) { 15 | cv::Mat R = cv::Mat(bayer_array.size() / 2, bayer_array.type()); 16 | cv::Mat Gr = cv::Mat(bayer_array.size() / 2, bayer_array.type()); 17 | cv::Mat Gb = cv::Mat(bayer_array.size() / 2, bayer_array.type()); 18 | cv::Mat B = cv::Mat(bayer_array.size() / 2, bayer_array.type()); 19 | ushort* p_R, * p_Gr, * p_Gb, * p_B, * p_bayer1, * p_bayer2; 20 | for (int i = 0; i < bayer_array.rows / 2; ++i) { 21 | p_R = R.ptr(i); 22 | p_Gr = Gr.ptr(i); 23 | p_Gb = Gb.ptr(i); 24 | p_B = B.ptr(i); 25 | p_bayer1 = bayer_array.ptr(2 * i); 26 | p_bayer2 = bayer_array.ptr(2 * i + 1); 27 | for (int j = 0; j < bayer_array.cols / 2; ++j) { 28 | if (bayer_pattern == "gbrg") { 29 | p_R[j] = p_bayer1[2 * j + 1]; 30 | p_Gr[j] = p_bayer2[2 * j + 1]; 31 | p_Gb[j] = p_bayer1[2 * j]; 32 | p_B[j] = p_bayer2[2 * j]; 33 | } 34 | else if (bayer_pattern == "rggb") { 35 | p_R[j] = p_bayer1[2 * j]; 36 | p_Gr[j] = p_bayer2[2 * j]; 37 | p_Gb[j] = p_bayer1[2 * j + 1]; 38 | p_B[j] = p_bayer2[2 * j + 1]; 39 | } 40 | else if (bayer_pattern == "bggr") { 41 | p_R[j] = p_bayer2[2 * j + 1]; 42 | p_Gr[j] = p_bayer1[2 * j + 1]; 43 | p_Gb[j] = p_bayer2[2 * j]; 44 | p_B[j] = p_bayer1[2 * j]; 45 | } 46 | else if (bayer_pattern == "grbg") { 47 | p_R[j] = p_bayer2[2 * j]; 48 | p_Gr[j] = p_bayer1[2 * j]; 49 | p_Gb[j] = p_bayer2[2 * j + 1]; 50 | p_B[j] = p_bayer1[2 * j + 1]; 51 | } 52 | else { 53 | throw "bayer pattern is not declared!\n"; 54 | } 55 | } 56 | } 57 | return std::vector { R,Gr,Gb,B }; 58 | } 59 | 60 | cv::Mat reconstruct_bayer(std::vector& sub_arrays, std::string& bayer_pattern) { 61 | cv::Mat bayer_array = cv::Mat(sub_arrays[0].size() * 2, sub_arrays[0].type()); 62 | ushort* p_R, * p_Gr, * p_Gb, * p_B, * p_bayer1, * p_bayer2; 63 | for (int i = 0; i < sub_arrays[0].rows; ++i) { 64 | p_R = sub_arrays[0].ptr(i); 65 | p_Gr = sub_arrays[1].ptr(i); 66 | p_Gb = sub_arrays[2].ptr(i); 67 | p_B = sub_arrays[3].ptr(i); 68 | p_bayer1 = bayer_array.ptr(2 * i); 69 | p_bayer2 = bayer_array.ptr(2 * i + 1); 70 | for (int j = 0; j < sub_arrays[0].cols; ++j) { 71 | if (bayer_pattern == "gbrg") { 72 | p_bayer1[2 * j + 1] = p_R[j]; 73 | p_bayer2[2 * j + 1] = p_Gr[j]; 74 | p_bayer1[2 * j] = p_Gb[j]; 75 | p_bayer2[2 * j] = p_B[j]; 76 | } 77 | else if (bayer_pattern == "rggb") { 78 | p_bayer1[2 * j] = p_R[j]; 79 | p_bayer2[2 * j] = p_Gr[j]; 80 | p_bayer1[2 * j + 1] = p_Gb[j]; 81 | p_bayer2[2 * j + 1] = p_B[j]; 82 | } 83 | else if (bayer_pattern == "bggr") { 84 | p_bayer2[2 * j + 1] = p_R[j]; 85 | p_bayer1[2 * j + 1] = p_Gr[j]; 86 | p_bayer2[2 * j] = p_Gb[j]; 87 | p_bayer1[2 * j] = p_B[j]; 88 | } 89 | else if (bayer_pattern == "grbg") { 90 | p_bayer2[2 * j] = p_R[j]; 91 | p_bayer1[2 * j] = p_Gr[j]; 92 | p_bayer2[2 * j + 1] = p_Gb[j]; 93 | p_bayer1[2 * j + 1] = p_B[j]; 94 | } 95 | else { 96 | throw "bayer pattern is not declared!\n"; 97 | } 98 | } 99 | } 100 | return bayer_array; 101 | } 102 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "libraw.h" 6 | #include "module.h" 7 | 8 | int main() { 9 | cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_ERROR); 10 | clock_t begin = clock(); 11 | 12 | const char* file = "img.dng"; 13 | // open dng file 14 | LibRaw* iProcessor = new LibRaw; 15 | iProcessor->open_file(file); 16 | iProcessor->unpack(); 17 | 18 | // raw2Mat and cut2roi 19 | cv::Mat raw = cv::Mat(iProcessor->imgdata.sizes.raw_height, iProcessor->imgdata.sizes.raw_width, CV_16UC1, iProcessor->imgdata.rawdata.raw_image); 20 | raw = raw(cv::Rect(iProcessor->imgdata.sizes.left_margin, iProcessor->imgdata.sizes.top_margin, iProcessor->imgdata.sizes.width, iProcessor->imgdata.sizes.height)); 21 | 22 | // init parameter 23 | uchar dpc_thres = 30; 24 | std::string BAYER = "rggb"; 25 | float black = iProcessor->imgdata.color.dng_levels.dng_black; 26 | float white = iProcessor->imgdata.color.dng_levels.dng_whitelevel[0]; 27 | cv::Mat aaf_kernel = (cv::Mat_(5, 5) << 28 | 1, 0, 1, 0, 1, 29 | 0, 0, 0, 0, 0, 30 | 1, 0, 8, 0, 1, 31 | 0, 0, 0, 0, 0, 32 | 1, 0, 1, 0, 1) / 16; 33 | std::vector parameter(iProcessor->imgdata.color.dng_levels.asshotneutral, iProcessor->imgdata.color.dng_levels.asshotneutral + 3); 34 | ushort CLIP = 4095; 35 | uchar bnr_size = 3; 36 | cv::Mat ccmatrix = cv::Mat(3, 4, CV_32F, iProcessor->imgdata.color.cmatrix)(cv::Rect(0, 0, 3, 3)); 37 | float gamma = 0.42; 38 | float RGB2YUV420[3][4] = { 39 | {0.257, 0.504, 0.098, 16}, 40 | {-.148, -.291, 0.439, 128 }, 41 | {0.439, -.368, -.071, 128}, 42 | }; 43 | uchar nlm_dw = 1; 44 | uchar nlm_Dw = 3; 45 | uchar nlm_thres = 10; 46 | uchar bnf_s_p = 20; 47 | uchar bnf_s_s = 20; 48 | char edge_filter[3][5] = { 49 | {-1, 0, -1, 0, -1}, 50 | {-1, 0, 8, 0, -1}, 51 | {-1, 0, -1, 0, -1} }; 52 | uchar brightness = 10; 53 | uchar contrast = 10; 54 | 55 | dpc(raw, dpc_thres); //DPC 56 | blc(raw, BAYER, black, white); //BLC 57 | aaf(raw, aaf_kernel); //AAF 58 | wbgc(raw, BAYER, parameter, CLIP); //WBGC 59 | bnr(raw, BAYER, bnr_size); //BNR 60 | cfa(raw, BAYER); //CFA 61 | ccm(raw, ccmatrix); //CCM 62 | gc(raw, gamma, CLIP); //GC 63 | cv::Mat raw_ = raw.clone(); 64 | raw_.convertTo(raw_, CV_8UC3); 65 | cv::imwrite("result_raw.png", raw_); 66 | 67 | cv::Mat y = cv::Mat(cv::Size(raw.cols, raw.rows), CV_16U); 68 | cv::Mat u = cv::Mat(cv::Size(raw.cols, raw.rows), CV_16U); 69 | cv::Mat v = cv::Mat(cv::Size(raw.cols, raw.rows), CV_16U); 70 | csc(raw, y, u, v, RGB2YUV420); //CSC 71 | nlm(y, nlm_dw, nlm_Dw, nlm_thres, CLIP); //NLM 72 | bnf(y, bnf_s_s, bnf_s_p, CLIP); //BNF 73 | cv::Mat edge_map = cv::Mat(cv::Size(y.cols, y.rows), CV_16U); 74 | ee(y, edge_map, edge_filter, CLIP); //EE 75 | 76 | bcc(y, brightness, contrast, CLIP); //BCC 77 | yuv2rgb(raw, y, u, v); //2RGB 78 | raw.convertTo(raw, CV_8UC3); 79 | cv::imwrite("result.png", raw); 80 | y.convertTo(y, CV_8UC1); 81 | cv::imwrite("result_y.png", y); 82 | u.convertTo(u, CV_8UC1); 83 | cv::imwrite("result_u.png", u); 84 | v.convertTo(v, CV_8UC1); 85 | cv::imwrite("result_v.png", v); 86 | edge_map.convertTo(edge_map, CV_8UC1); 87 | cv::imwrite("result_edgemap.png", edge_map); 88 | 89 | clock_t end = clock(); 90 | std::cout << "-------------------------------------------" << std::endl; 91 | std::cout << "ISP Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC << " s." << std::endl; 92 | // recycle LibRaw and delete ptr 93 | iProcessor->recycle(); 94 | delete iProcessor; 95 | } -------------------------------------------------------------------------------- /src/module.cpp: -------------------------------------------------------------------------------- 1 | #include "module.h" 2 | 3 | void dpc(cv::Mat& src, uchar thres) 4 | { 5 | clock_t begin = clock(); 6 | uchar pads[4] = { 2, 2, 2, 2 }; 7 | cv::Mat src_p = padding(src, pads); 8 | ushort* p0, * p1, * p2, * p3, * p4, * p5, * p6, * p7, * p8, * p; 9 | int grad_h1 = 0, grad_h2 = 0, grad_h3 = 0, grad_v1 = 0, grad_v2 = 0, grad_v3 = 0; 10 | int grad_45_1 = 0, grad_45_2 = 0, grad_45_3 = 0, grad_135_1 = 0, grad_135_2 = 0, grad_135_3 = 0; 11 | int grad_h = 0, grad_v = 0, grad_45 = 0, grad_135 = 0; 12 | std::vector gradient(4, 0); 13 | int grad_sum = 0; 14 | for (int i = 0; i < src_p.rows - 4; ++i) { 15 | std::cout << "\r" << "DPC: "; 16 | std::cout << std::setw(6) << std::fixed << std::setprecision(2) << (float)i / (src_p.rows - 2) * 100 << "%"; 17 | p0 = src_p.ptr(i + 2); 18 | p1 = src_p.ptr(i); 19 | p2 = src_p.ptr(i); 20 | p3 = src_p.ptr(i); 21 | p4 = src_p.ptr(i + 2); 22 | p5 = src_p.ptr(i + 2); 23 | p6 = src_p.ptr(i + 4); 24 | p7 = src_p.ptr(i + 4); 25 | p8 = src_p.ptr(i + 4); 26 | p = src.ptr(i); 27 | for (int j = 0; j < src_p.cols - 4; j++) { 28 | grad_h1 = abs(p1[j] + p3[j + 4] - 2 * p2[j + 2]); 29 | grad_h2 = abs(p4[j] + p5[j + 4] - 2 * p0[j + 2]); 30 | grad_h3 = abs(p6[j] + p8[j + 4] - 2 * p7[j + 2]); 31 | grad_v1 = abs(p1[j] + p4[j] - 2 * p6[j]); 32 | grad_v2 = abs(p2[j + 2] + p7[j + 2] - 2 * p0[j + 2]); 33 | grad_v3 = abs(p3[j + 4] + p8[j + 4] - 2 * p5[j + 4]); 34 | grad_45_1 = 2 * abs(p2[j + 2] - p4[j]); 35 | grad_45_2 = abs(p3[j + 4] + p6[j] - 2 * p0[j + 2]); 36 | grad_45_3 = 2 * abs(p5[j + 4] - p7[j + 2]); 37 | grad_135_1 = 2 * abs(p2[j + 2] -p5[j + 4]); 38 | grad_135_2 = abs(p1[j] + p8[j + 4] - 2 * p0[j + 2]); 39 | grad_135_3 = 2 * abs(p4[j] - p7[j + 2]); 40 | 41 | grad_h = median(grad_h1, grad_h2, grad_h3); 42 | grad_v = median(grad_v1, grad_v2, grad_v3); 43 | grad_45 = median(grad_45_1, grad_45_2, grad_45_3); 44 | grad_135 = median(grad_135_1, grad_135_2, grad_135_3); 45 | gradient = { grad_h, grad_v, grad_45, grad_135 }; 46 | auto minPosition = std::min_element(gradient.begin(), gradient.end()); 47 | if (minPosition == gradient.begin() && grad_h2 > 4*(grad_h1+grad_h3)) 48 | { 49 | if (abs(p4[j] - p0[j + 2]) < abs(p0[j + 2] - p5[j + 4])) { 50 | p[j] = p4[j] + (p2[j + 2] + p7[j + 2] - p1[j] - p6[j]) / 2; 51 | } 52 | else { 53 | p[j] = p5[j + 4] + (p2[j + 2] + p7[j + 2] - p3[j + 4] - p8[j + 4]) / 2; 54 | } 55 | } 56 | if (minPosition == gradient.begin()+1 && grad_v2 > 4 * (grad_v1 + grad_v3)) 57 | { 58 | if (abs(p2[j + 2] - p0[j + 2]) < abs(p0[j + 2] - p7[j + 2])) { 59 | p[j] = p2[j + 2] + (p4[j] + p5[j + 4] - p1[j] - p3[j + 4]) / 2; 60 | } 61 | else { 62 | p[j] = p7[j + 2] + (p4[j] + p5[j + 4] - p6[j] - p8[j + 4]) / 2; 63 | } 64 | } 65 | if (minPosition == gradient.begin()+2) 66 | { 67 | grad_sum = abs(grad_135_1 - grad_135_2) + abs(grad_135_1 - grad_135_3) + abs(grad_135_2 - grad_135_3); 68 | if (grad_sum > 100) { 69 | if (grad_45_2 > 3 * (grad_45_1 + grad_45_3) && grad_135_2 > 3 * (grad_135_1 + grad_135_3)) { 70 | if (abs(p3[j + 4] - p0[j + 2]) < abs(p0[j + 2] - p6[j])) { 71 | p[j] = p3[j + 4] + (p4[j] + p7[j + 2] - p2[j + 2] - p5[j + 4]) / 2; 72 | } 73 | else { 74 | p[j] = p6[j] - (p4[j] + p7[j + 2] - p2[j + 2] - p5[j + 4]) / 2; 75 | } 76 | } 77 | } 78 | else { 79 | if (grad_45_2 > 3 * (grad_45_1 + grad_45_3)) { 80 | if (abs(p3[j + 4] - p0[j + 2]) < abs(p0[j + 2] - p6[j])) { 81 | p[j] = p3[j + 4] + (p4[j] + p7[j + 2] - p2[j + 2] - p5[j + 4]) / 2; 82 | } 83 | else { 84 | p[j] = p6[j] - (p4[j] + p7[j + 2] - p2[j + 2] - p5[j + 4]) / 2; 85 | } 86 | } 87 | } 88 | } 89 | if (minPosition == gradient.begin()+3) 90 | { 91 | grad_sum = abs(grad_45_1 - grad_45_2) + abs(grad_45_1 - grad_45_3) + abs(grad_45_2 - grad_45_3); 92 | if (grad_sum > 100) { 93 | if (grad_135_2 > 3 * (grad_135_1 + grad_135_3) && grad_45_2 > 3 * (grad_45_1 + grad_45_3)) { 94 | if (abs(p1[j] - p0[j + 2]) < abs(p0[j + 2] - p8[j + 4])) { 95 | p[j] = p1[j] + (p5[j + 4] + p6[j] - p2[j + 2] - p4[j]) / 2; 96 | } 97 | else { 98 | p[j] = p8[j + 4] - (p5[j + 4] + p6[j] - p2[j + 2] - p4[j]) / 2; 99 | } 100 | } 101 | } 102 | else { 103 | if (grad_135_2 > 3 * (grad_135_1 + grad_135_3)) { 104 | if (abs(p1[j] - p0[j + 2]) < abs(p0[j + 2] - p8[j + 4])) { 105 | p[j] = p1[j] + (p5[j + 4] + p6[j] - p2[j + 2] - p4[j]) / 2; 106 | } 107 | else { 108 | p[j] = p8[j + 4] - (p5[j + 4] + p6[j] - p2[j + 2] - p4[j]) / 2; 109 | } 110 | } 111 | } 112 | } 113 | 114 | } 115 | } 116 | clock_t end = clock(); 117 | std::cout << "\r" << "DPC Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms." << std::endl; 118 | } 119 | 120 | void blc(cv::Mat& src, std::string bayer_pattern, float black_level, float white_level) 121 | { 122 | clock_t begin = clock(); 123 | std::vector bayer4 = split_bayer(src, bayer_pattern); 124 | int pixel_R = 0, pixel_Gr = 0, pixel_Gb = 0, pixel_B = 0; 125 | ushort* p_R, * p_Gr, * p_Gb, * p_B; 126 | for (int i = 0; i < bayer4[0].rows; ++i) { 127 | p_R = bayer4[0].ptr(i); 128 | p_Gr = bayer4[1].ptr(i); 129 | p_Gb = bayer4[2].ptr(i); 130 | p_B = bayer4[3].ptr(i); 131 | for (int j = 0; j < bayer4[0].cols; ++j) { 132 | pixel_R = p_R[j] - black_level; 133 | pixel_B = p_B[j] - black_level; 134 | pixel_Gr = p_Gr[j] - black_level; 135 | pixel_Gb = p_Gb[j] - black_level; 136 | 137 | p_R[j] = ((pixel_R < 0) ? 0 : (pixel_R > white_level ? white_level : pixel_R)); 138 | p_Gr[j] = ((pixel_Gr < 0) ? 0 : (pixel_Gr > white_level ? white_level : pixel_Gr)); 139 | p_Gb[j] = ((pixel_Gb < 0) ? 0 : (pixel_Gb > white_level ? white_level : pixel_Gb)); 140 | p_B[j] = ((pixel_B < 0) ? 0 : (pixel_B > white_level ? white_level : pixel_B)); 141 | } 142 | } 143 | src = reconstruct_bayer(bayer4, bayer_pattern); 144 | clock_t end = clock(); 145 | std::cout << "BLC Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms." << std::endl; 146 | } 147 | 148 | void aaf(cv::Mat& src, cv::Mat& kernel) 149 | { 150 | clock_t begin = clock(); 151 | cv::Mat origin_img = src.clone(); 152 | cv::filter2D(origin_img, src, CV_16UC1, kernel); 153 | clock_t end = clock(); 154 | std::cout << "AAF Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms."<< std::endl; 155 | } 156 | 157 | void wbgc(cv::Mat& src, std::string bayer_pattern, std::vector gain, ushort clip) 158 | { 159 | gain = { 1 / gain[0] * 1024, 1 / gain[1] * 1024, 1 / gain[2] * 1024 }; 160 | clock_t begin = clock(); 161 | std::vector rggb = split_bayer(src, bayer_pattern); 162 | rggb[0] *= (gain[0] / 1024); 163 | rggb[1] *= (gain[1] / 1024); 164 | rggb[2] *= (gain[1] / 1024); 165 | rggb[3] *= (gain[2] / 1024); 166 | src = reconstruct_bayer(rggb, bayer_pattern); 167 | ushort* p_img; 168 | for (int i = 0; i < src.rows; ++i) { 169 | p_img = src.ptr(i); 170 | for (int j = 0; j < src.cols; ++j) { 171 | p_img[j] = (p_img[j] < 0) ? 0 : (p_img[j] > clip ? clip : p_img[j]); 172 | } 173 | } 174 | clock_t end = clock(); 175 | std::cout << "AWB Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms." << std::endl; 176 | } 177 | 178 | void bnr(cv::Mat& src, std::string bayer_pattern, uchar ksize) { 179 | clock_t begin = clock(); 180 | std::vector rggb = split_bayer(src, bayer_pattern); 181 | medianBlur(rggb[0], rggb[0], ksize); 182 | medianBlur(rggb[1], rggb[1], ksize); 183 | medianBlur(rggb[2], rggb[2], ksize); 184 | medianBlur(rggb[3], rggb[3], ksize); 185 | src = reconstruct_bayer(rggb, bayer_pattern); 186 | clock_t end = clock(); 187 | std::cout << "BNF Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms." << std::endl; 188 | } 189 | 190 | void cfa(cv::Mat& src, std::string bayer_pattern) 191 | { 192 | clock_t begin = clock(); 193 | if (bayer_pattern == "gbrg") { 194 | cv::cvtColor(src, src, cv::COLOR_BayerGB2RGB); 195 | } 196 | else if (bayer_pattern == "rggb") { 197 | cv::cvtColor(src, src, cv::COLOR_BayerRG2RGB); 198 | } 199 | else if (bayer_pattern == "bggr") { 200 | cv::cvtColor(src, src, cv::COLOR_BayerBG2RGB); 201 | } 202 | else if (bayer_pattern == "grbg") { 203 | cv::cvtColor(src, src, cv::COLOR_BayerGR2RGB); 204 | } 205 | else { 206 | throw "bayer pattern is not declared!\n"; 207 | } 208 | clock_t end = clock(); 209 | std::cout << "CFA Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms." << std::endl; 210 | } 211 | 212 | void ccm(cv::Mat& src, cv::Mat& ccm) 213 | { 214 | clock_t begin = clock(); 215 | cv::Mat img0 = src.clone(); 216 | cv::Vec3s* p, * p0; 217 | for (int i = 0; i < src.rows; ++i) { 218 | p0 = img0.ptr(i); 219 | p = src.ptr(i); 220 | for (int j = 0; j < src.cols; ++j) { 221 | for (int k = 0; k < 3; ++k) { 222 | p[j][k] = ccm.ptr(k)[0] * p0[j][0] + ccm.ptr(k)[1] * p0[j][1] + ccm.ptr(k)[2] * p0[j][2]; 223 | } 224 | } 225 | } 226 | clock_t end = clock(); 227 | std::cout << "CCM Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms." << std::endl; 228 | } 229 | 230 | void gc(cv::Mat& src, float gamma, ushort clip) 231 | { 232 | clock_t begin = clock(); 233 | std::vector LUT{}; 234 | for (int i = 0; i < clip + 1; ++i) { 235 | float lx = std::pow(((float)i / clip), (float)gamma) * (float)sdr_max_value; 236 | LUT.push_back((int)lx); 237 | } 238 | 239 | cv::Vec3s* p; 240 | for (int i = 0; i < src.rows; ++i) { 241 | p = src.ptr(i); 242 | for (int j = 0; j < src.cols; ++j) { 243 | for (int k = 0; k < 3; ++k) { 244 | p[j][k] = ((p[j][k] < 0) ? ushort(LUT[0]) : (p[j][k] > clip ? ushort(LUT[clip]) : ushort(LUT[p[j][k]]))); 245 | } 246 | } 247 | } 248 | clock_t end = clock(); 249 | std::cout << "GaC Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms." << std::endl; 250 | } 251 | 252 | void csc(cv::Mat& src, cv::Mat& y, cv::Mat& u, cv::Mat& v, float YUV420[3][4]) 253 | { 254 | clock_t begin = clock(); 255 | cv::Vec3s* p; 256 | float* p_YUV420; 257 | ushort* p_y, * p_u, * p_v; 258 | for (int i = 0; i < src.rows; ++i) { 259 | p = src.ptr(i); 260 | p_y = y.ptr(i); 261 | p_u = u.ptr(i); 262 | p_v = v.ptr(i); 263 | for (int j = 0; j < src.cols; ++j) { 264 | p_y[j] = (p[j][0] * YUV420[0][0] + p[j][1] * YUV420[0][1] + p[j][2] * YUV420[0][2] + YUV420[0][3]); 265 | p_u[j] = (p[j][0] * YUV420[1][0] + p[j][1] * YUV420[1][1] + p[j][2] * YUV420[1][2] + YUV420[1][3]); 266 | p_v[j] = (p[j][0] * YUV420[2][0] + p[j][1] * YUV420[2][1] + p[j][2] * YUV420[2][2] + YUV420[2][3]); 267 | } 268 | } 269 | 270 | clock_t end = clock(); 271 | std::cout << "CSC Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms." << std::endl; 272 | } 273 | 274 | void nlm(cv::Mat& src, uchar ds, uchar Ds, uchar h, uchar clip) 275 | { 276 | clock_t begin = clock(); 277 | ushort center_y = 0, center_x = 0, start_y = 0, start_x = 0, pixel_pad = 0; 278 | double sweight = .0, average = .0, wmax = .0, w = .0, dist = .0, pixel = 0; 279 | ushort* p_src, *p_p, *p_p0, * p_neighbor, * p_center_w; 280 | 281 | uchar pads[4] = { Ds, Ds, Ds, Ds }; 282 | cv::Mat src_p = padding(src, pads); 283 | for (int y = 0; y < src_p.rows - 2 * Ds; ++y) { 284 | std::cout << "\r" << "NLM: "; 285 | std::cout << std::setw(6) << std::fixed << std::setprecision(2) << (float)y / (src_p.rows - 2) * 100 << "%"; 286 | center_y = y + Ds; 287 | p_src = src.ptr(y); 288 | p_p0 = src_p.ptr(center_y); 289 | for (int x = 0; x < src_p.cols - 2 * Ds; ++x) { 290 | center_x = x + Ds; 291 | //calWeights 292 | sweight = .0, average = .0, wmax = .0, dist = .0; 293 | for (int m = 0; m < 2 * Ds + 1 - 2 * ds - 1; ++m) { 294 | start_y = center_y - Ds + ds + m; 295 | p_p = src_p.ptr(start_y); 296 | for (int n = 0; n < 2 * Ds + 1 - 2 * ds - 1; ++n) { 297 | start_x = center_x - Ds + ds + n; 298 | pixel_pad = p_p[start_x]; 299 | if (m != center_y || n != center_x) { 300 | for (int i = 0; i < 2 * ds + 1; ++i) { 301 | p_neighbor = src_p.ptr(start_y - ds + i); 302 | p_center_w = src_p.ptr(center_y - ds + i); 303 | for (int j = 0; j < 2 * ds + 1; ++j) { 304 | dist += (((p_neighbor[start_x - ds + j] - p_center_w[center_x - ds + j]) ^ 2) / (2 * ds + 1) ^ 2); 305 | } 306 | } 307 | w = exp(-dist / (h ^ 2)); 308 | if (w > wmax) wmax = w; 309 | sweight += w; 310 | average += (w * pixel_pad); 311 | } 312 | } 313 | } 314 | average += (wmax * p_p0[center_x]); 315 | sweight += wmax; 316 | pixel = average / sweight; 317 | pixel = (pixel <= 0) ? 0 : (pixel >= clip ? clip : pixel); 318 | p_src[x] = (ushort)pixel; 319 | } 320 | } 321 | clock_t end = clock(); 322 | std::cout << "\r" << "NLM Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms." << std::endl; 323 | } 324 | 325 | void bnf(cv::Mat& src, uchar sigmoid_s, uchar sigmoid_p, ushort clip) 326 | { 327 | clock_t begin = clock(); 328 | 329 | uchar pads[4] = { 2, 2, 2, 2 }; 330 | cv::Mat src_p = padding(src, pads); 331 | int sum_weight = 0, sum_imgA = 0; 332 | ushort pixel_center = 0; 333 | double dive = .0, w = .0; 334 | ushort* p, * p_p; 335 | 336 | for (int y = 0; y < src_p.rows - 4; ++y) { 337 | std::cout << "\r" << "BNF: "; 338 | std::cout << std::setw(6) << std::fixed << std::setprecision(2) << (float)y / (src_p.rows - 4) * 100 << "%"; 339 | p = src.ptr(y); 340 | for (int x = 0; x < src_p.cols - 4; ++x) { 341 | pixel_center = src_p.ptr(y + 2)[x + 2]; 342 | sum_imgA = 0; 343 | sum_weight = 0; 344 | dive = .0; 345 | for (int i = 0; i < 5; ++i) { 346 | p_p = src_p.ptr(y + i); 347 | for (int j = 0; j < 5; ++j) { 348 | w = exp(-((abs(p_p[x + j] - pixel_center)) ^ 2) / 2 / sigmoid_p ^ 2) * exp(-((2 - i) ^ 2 + (2 - j) ^ 2) / 2 / sigmoid_s); 349 | sum_imgA += p_p[x + j] * w; 350 | sum_weight += w; 351 | } 352 | } 353 | dive = sum_imgA / sum_weight; 354 | dive = (dive < 0) ? 0 : (dive > clip ? clip : dive); 355 | p[x] = (ushort)dive; 356 | } 357 | } 358 | clock_t end = clock(); 359 | std::cout << "\r" << "BNF Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms." << std::endl; 360 | } 361 | 362 | void ee(cv::Mat& src, cv::Mat& edgemap, char edge_filter[3][5], ushort clip) 363 | { 364 | clock_t begin = clock(); 365 | 366 | uchar pads[4] = { 1,1,2,2 }; 367 | cv::Mat src_p = padding(src, pads); 368 | double em_img, ee_img, tmp_em_img; 369 | ushort* p_src, * p_edgemap; 370 | for (int y = 0; y < src_p.rows - 2; ++y) { 371 | std::cout << "\r" << "EEH: "; 372 | std::cout << std::setw(6) << std::fixed << std::setprecision(2) << (float)y / (src_p.rows - 4) * 100 << "%"; 373 | p_edgemap = edgemap.ptr(y); 374 | p_src = src.ptr(y); 375 | for (int x = 0; x < src_p.cols - 4; ++x) { 376 | em_img = 0.0; 377 | for (int i = 0; i < 3; ++i) { 378 | for (int j = 0; j < 5; ++j) { 379 | em_img += src_p.ptr(y + i)[x + j] * edge_filter[i][j]; 380 | } 381 | } 382 | em_img = em_img / 8; 383 | ee_img = src_p.ptr(y + 1)[x + 2] + em_img; 384 | ee_img = (ee_img < 0) ? 0 : (ee_img > clip ? clip : ee_img); 385 | p_edgemap[x] = (ushort)em_img; 386 | p_src[x] = (ushort)ee_img; 387 | } 388 | } 389 | clock_t end = clock(); 390 | std::cout << "\r" << "EEH Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms." << std::endl; 391 | } 392 | 393 | void bcc(cv::Mat& src, uchar brightness, uchar contrast, ushort bcc_clip) 394 | { 395 | clock_t begin = clock(); 396 | contrast = contrast / (2 ^ 5); 397 | ushort* p; 398 | int pixel; 399 | for (int y = 0; y < src.rows; ++y) { 400 | p = src.ptr(y); 401 | for (int x = 0; x < src.cols; ++x) { 402 | pixel = p[x]; 403 | pixel = pixel + brightness + (pixel - 127) * contrast; 404 | pixel = (pixel < 0) ? 0 : (pixel > bcc_clip ? bcc_clip : pixel); 405 | p[x] = (ushort)pixel; 406 | } 407 | } 408 | clock_t end = clock(); 409 | std::cout << "BCC Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms." << std::endl; 410 | } 411 | 412 | void yuv2rgb(cv::Mat& src, cv::Mat& y, cv::Mat& u, cv::Mat& v) 413 | { 414 | clock_t begin = clock(); 415 | ushort* p_y, * p_u, * p_v; 416 | cv::Vec3s* p; 417 | for (int i = 0; i < src.rows; ++i){ 418 | p = src.ptr(i); 419 | p_y = y.ptr(i); 420 | p_u = u.ptr(i); 421 | p_v = v.ptr(i); 422 | for (int j = 0; j < src.cols; ++j){ 423 | p[j][0] = cv::saturate_cast(p_y[j] + 1.402 * (p_v[j] - 128)); // R 424 | p[j][1] = cv::saturate_cast(p_y[j] - 0.34413 * (p_u[j] - 128) - 0.71414 * (p_v[j] - 128)); // G 425 | p[j][2] = cv::saturate_cast(p_y[j] + 1.772 * (p_u[j] - 128)); // B 426 | } 427 | } 428 | clock_t end = clock(); 429 | std::cout << "Y2R Done! Elapsed " << double(end - begin) / CLOCKS_PER_SEC * 1000 << " ms." << std::endl; 430 | 431 | } 432 | --------------------------------------------------------------------------------