一、定义章节第3章 灰度变换与空间滤波 → 3.2 基本灰度变换 → 3.2.3 幂律伽马变换别名幂律变换Power‑Law Transformation、伽马变换Gamma Transformation公式[scrγ] [ s c r^{\gamma} ][scrγ](r)输入灰度归一化到 ([0,1])(s)输出灰度归一化后再映射回0–255(c0)比例常数常用 (c1)(gamma0)伽马值控制曲线形状二、不同γ值的效果1γ 1如 0.4、0.6、0.8曲线上凸在对角线之上提亮图像、扩展暗部、压缩亮部适用整体偏暗、欠曝、暗部细节缺失的图像2γ 1直线 (sr)恒等变换无效果3γ 1如 1.5、2.0、2.5曲线下凹在对角线之下压暗图像、压缩暗部、扩展亮部适用整体偏亮、过曝、亮部细节缺失的图像一句话记γ小提亮、救暗γ大压暗、救亮[scrγ] [ s c r^{\gamma} ][scrγ]三、与对数变换的区别对数变换[(sc∗log(1r))] [(sc * log(1r))][(sc∗log(1r))]强扩展暗部、压缩亮部固定趋势幂律变换通过调节γ可模拟对数γ1或反向效果γ1更灵活四、典型应用伽马校正Gamma Correction显示器/相机/打印机的光电响应是非线性幂律显示器通常γ≈2.2导致图像偏暗用γ≈0.45校正使显示线性欠曝/过曝修复夜景/暗图γ0.50.8强光/过曝γ1.52.5对比度微调不做剧烈拉伸用γ小幅度调整明暗层次五、代码实现kompute实现shader核心代码#version 450layout(constant_id0)const uint WIDTH3840;layout(constant_id1)const uint HEIGHT2160;layout(local_size_x16, local_size_y16)in;layout(push_constant)uniform PushConstants{float gamma;}pc;layout(set0, binding0)buffer inputBuffer{float inputData[];};layout(set0, binding1)writeonly buffer outputBuffer{float outputData[];};voidmain(){uint xgl_GlobalInvocationID.x;uint ygl_GlobalInvocationID.y;if(xWIDTH||yHEIGHT){return;}uint indexy * WIDTH x;float grayinputData[index];float c1.0;float gammaGrayc * pow(gray, pc.gamma);outputData[index]gammaGray;}C核心代码intmain(intargc,char*argv[]){constintchannels1;std::string inputPath;floatgamma2.2f;if(argc1){inputPathargv[1];}else{inputPathD:/tengyanbo/repo/kompute/examples/grayscale/build/Release/output.png;}if(argc2){gammastd::atof(argv[2]);}std::coutstd::endl;std::cout Image Gamma Transform Processing std::endl;std::cout s c * r^gamma std::endl;std::coutstd::endl;std::coutstd::endl;std::coutInput image path: inputPathstd::endl;std::coutGamma value: gammastd::endl;intimgWidth,imgHeight,imgChannels;unsignedchar*imgDatastbi_load(inputPath.c_str(),imgWidth,imgHeight,imgChannels,0);if(!imgData){std::coutFailed to load input image: stbi_failure_reason()std::endl;std::coutCreating test grayscale pattern...std::endl;imgWidth512;imgHeight512;imgChannels1;imgData(unsignedchar*)malloc(imgWidth*imgHeight);for(inty0;yimgHeight;y){for(intx0;ximgWidth;x){intidxy*imgWidthx;imgData[idx](unsignedchar)((x*255)/imgWidth);}}}std::coutImage size: imgWidth x imgHeightstd::endl;std::coutOriginal image channels: imgChannelsstd::endl;if(imgChannels!1){std::cout\n[ERROR] 伽马变换仅支持灰度图!std::endl;std::cout当前图像有 imgChannels 个通道, 不是灰度图。std::endl;std::cout请提供单通道灰度图像。std::endl;stbi_image_free(imgData);return1;}std::cout[OK] 检测到灰度图, 开始处理...std::endl;std::coutInput pixel [0] (int)imgData[0]std::endl;floatinputGrayimgData[0]/255.0f;std::vectorfloatinputData(imgWidth*imgHeight);for(inti0;iimgWidth*imgHeight;i){inputData[i]imgData[i]/255.0f;}stbi_image_free(imgData);std::coutTotal pixels: (imgWidth*imgHeight), Data size: inputData.size() floatsstd::endl;try{kp::Manager mgr;kp::Memory::MemoryTypes optimalTypedetectOptimalMemoryType(mgr);std::coutstd::endl;autoinputTensormgr.tensorT(inputData,optimalType);autooutputDatastd::vectorfloat(imgWidth*imgHeight*channels,0.0f);autooutputTensormgr.tensorT(outputData,optimalType);std::vectorstd::shared_ptrkp::Memoryparams{inputTensor,outputTensor};std::vectoruint32_tshaderDatastd::vectoruint32_t(shader::GAMMA_TRANSFORM_COMP_SPV.begin(),shader::GAMMA_TRANSFORM_COMP_SPV.end());kp::Workgroup workgroup{(uint32_t)imgWidth,(uint32_t)imgHeight,1};std::vectoruint32_tspecConstants{(uint32_t)imgWidth,(uint32_t)imgHeight};std::vectorfloatpushConstants{gamma};autoalgomgr.algorithm(params,shaderData,workgroup,specConstants,pushConstants);std::cout\n 执行GPU计算 std::endl;autostartstd::chrono::high_resolution_clock::now();mgr.sequence()-recordkp::OpSyncDevice(params)-recordkp::OpAlgoDispatch(algo)-recordkp::OpSyncLocal(params)-eval();autoendstd::chrono::high_resolution_clock::now();autodurationstd::chrono::duration_caststd::chrono::milliseconds(end-start).count();std::coutTotal GPU processing time: durationmsstd::endl;std::coutstd::endl;constautooutputVecoutputTensor-vector();std::coutOutput vector size: outputVec.size()std::endl;floatc1.0f;floatexpectedGrayc*std::pow(inputGray,gamma);std::coutExpected gamma transform value: expectedGraystd::endl;std::coutOutput pixel [0] (int)(outputVec[0]*255)std::endl;unsignedchar*outputImg(unsignedchar*)malloc(imgWidth*imgHeight);for(inti0;iimgWidth*imgHeight;i){outputImg[i](unsignedchar)(outputVec[i]*255.0f);}stbi_write_png(output_gamma.png,imgWidth,imgHeight,channels,outputImg,imgWidth*channels);std::coutOutput saved to output_gamma.png (grayscale)std::endl;free(outputImg);}catch(conststd::exceptione){std::cerrError: e.what()std::endl;return1;}return0;}当γ2.2时:当γ0.4时当γ1时也就是原图OpenCV实现importcv2importnumpyasnpdefpower_law_transform(img,gamma1.0,c1.0):# 归一化到 [0,1]rimg/255.0# 幂律变换sc*np.power(r,gamma)# 映射回 [0,255]snp.clip(s*255,0,255).astype(np.uint8)returns# 读取灰度图imgcv2.imread(test.jpg,0)# 不同γ测试img_brightpower_law_transform(img,gamma0.5)img_darkpower_law_transform(img,gamma2.0)六、核心要点公式(\boldsymbol{scr^{\gamma}})γ1提亮、扩暗、压亮γ1压暗、缩暗、扩亮与对数变换的差异幂律可调对数固定核心应用伽马校正、欠曝/过曝修复更多内容欢迎关注我的微信公众号:半夏之夜的无情剑客