资讯动态

OpenCV与Halcon实现白平衡:灰度世界与完美反射算法实战对比

发布时间:2026/10/5 3:13:46 来源:尧图企业网站定制
相机拍出来的照片偏蓝、偏黄这是每个做图像处理的人都会碰到的问题。特别是做机器视觉项目时光源色温不稳定、不同批次产品颜色有差异这时候白平衡处理就是绕不开的一步。我之前在项目中分别用OpenCV和Halcon实现过彩色图像的白平衡效果两者思路有相通之处但具体实现方式差异很大。这篇文章就把我实际调试过程中整理出来的方案、踩过的坑、以及两种工具各自的适用场景分享出来。先说清楚一个概念白平衡的本质是什么。简单说就是让图像中原本白色的物体在成片里看起来也是白的。人眼有色彩恒常性不管在日光下还是在白炽灯下看一张白纸都觉得是白的但相机传感器没有这个能力它会老老实实记录光线的色温所以就会偏色。白平衡算法要做的事情就是把这种偏色校正回来。工业场景里常用的方法有灰度世界假设、完美反射假设以及更复杂的基于边缘或学习的算法。这里我主要讲前两种因为它们实现简单、计算量小、在多数工业场景下够用也是我实际项目中使用最多的方案。围绕这两种算法我会分别给出OpenCV和Halcon的实现代码并对比它们在处理逻辑上的差异。如果你正在做类似的项目这篇文章可以作为直接参考。1. 先搞懂白平衡的两种经典算法灰度世界与完美反射在写代码之前必须先把算法原理理解透。你会发现OpenCV和Halcon的差异归根结底是两种工具对算法落地的表达方式不同原理是共通的。1.1 灰度世界假设灰度世界假设的核心思想是一幅色彩丰富的图像其RGB三个通道的平均值应该趋于同一个灰度值。用人话讲就是“整个世界平均下来是灰色的”。既然自然界中各种颜色的物体加起来统计上会趋向于灰色那么如果图像偏蓝蓝色通道的平均值就会偏高这时把蓝色通道整体压下来把红色通道整体提上去就能校正偏色。具体计算步骤很简单分别计算图像R、G、B三个通道的平均值avg_r、avg_g、avg_b。计算三个平均值的均值avg_gray (avg_r avg_g_b) / 3这个值就是期望的“灰色”。分别计算三个通道的增益gain_r avg_gray / avg_rgain_g avg_gray / avg_ggain_b avg_gray / avg_b。每个像素的各通道值乘以对应增益完成校正。这个算法在色彩丰富的自然图像上表现很好但如果场景里某一种颜色占主导地位比如一整片蓝天、一大片绿草地灰度世界假设就会失效校正结果会偏得很离谱。实际项目中如果客户的产品表面主色是单一大面积色块灰度世界算法就不太合适需要考虑其他方案或做区域掩膜处理。1.2 完美反射假设完美反射假设也叫白点算法换了一个思路图像中最亮的那个点理论上应该是白色的。因为如果场景中有白色物体或高光区域这些区域的RGB值应该接近相等。算法会找出图像中最亮的像素或前N%亮的像素按照这些像素的RGB值来推算整幅图像的偏色程度。计算步骤统计图像所有像素的亮度值通常用RGB中最大值或者转成灰度。取亮度最高的前1%或5%的像素计算这些像素的RGB平均值。用这些平均值的最大值作为参考白点的灰度值计算各通道增益。每个像素乘以对应增益完成校正。这个算法对高光区域明显、有镜面反射的场景效果好比如金属表面检测、玻璃瓶拍照。但如果图像本身过暗或者没有高光区域算法会丢失参考点增益计算不稳定。实际使用中我通常会把两种算法都实现根据场景切换比单一算法硬扛要稳得多。2. OpenCV实现方案基于遍历和矩阵运算的思路OpenCV实现白平衡有非常多的路径关键在于你追求的效果和速度。我在这里给出两种经典的实现方式并拆解每一步的计算逻辑方便你直接迁移到自己的项目里。2.1 环境准备与核心API理解OpenCV中我们用到的核心操作就是通道拆分、均值计算和像素运算。对于C环境需要包含opencv2/core.hpp和opencv2/imgproc.hppPython环境则直接import cv2和numpy。在开始实现之前有一个很重要的细节OpenCV读取图像后通道顺序是BGR不是RGB。如果直接遍历通道去算平均值分到的顺序是蓝色、绿色、红色很多人到了显示结果时发现颜色不对就是这里出了问题。虽然白平衡的增益计算对通道顺序没有那么敏感因为三个通道都参与了相同的数学运算只是映射的对象不同但为了代码易读性建议代码里显式说明通道顺序。另一个细节是OpenCV中图像数据类型的坑。如果图像是uint8类型0-255范围在计算增益时直接对像素做乘法结果很容易溢出。比如像素值是200增益是1.5相乘结果是300但uint8类型根本存不下300会出现数据截断导致图像出现不自然的色斑。解决方案有两种一是先把图像转换为float32再做运算最后再转回uint8二是使用OpenCV的convertTo配合归一化。我实际项目中一直用第一种方式稳定且可控。2.2 灰度世界算法代码实现及参数计算下面是基于灰度世界假设的OpenCV C实现这是我在实际项目里跑过多次的版本。void whiteBalanceGrayWorld(cv::Mat src, cv::Mat dst) { // 防止修改原图先克隆一份 src.copyTo(dst); if (dst.channels() ! 3) return; // 将图像转为浮点型避免乘增益时数据溢出 cv::Mat floatImg; dst.convertTo(floatImg, CV_32FC3); // 拆分成三个通道注意顺序是BGR std::vectorcv::Mat channels; cv::split(floatImg, channels); // 计算每个通道的平均值 double avgR cv::mean(channels[2])[0]; double avgG cv::mean(channels[1])[0]; double avgB cv::mean(channels[0])[0]; // 期望的灰色像素值三个通道平均值的均值 double avgGray (avgR avgG avgB) / 3.0; // 计算各通道增益 double gainR avgGray / avgR; double gainG avgGray / avgG; double gainB avgGray / avgB; // 像素乘增益 channels[2] channels[2] * gainR; channels[1] channels[1] * gainG; channels[0] channels[0] * gainB; // 合并通道转回uint8 cv::merge(channels, dst); dst.convertTo(dst, CV_8UC3); }Python版本同样简洁这里也一并放出方便做算法验证和原型测试。import cv2 import numpy as np def white_balance_gray_world(img): result img.astype(np.float32) avg_b np.mean(result[:, :, 0]) avg_g np.mean(result[:, :, 1]) avg_r np.mean(result[:, :, 2]) avg_gray (avg_b avg_g avg_r) / 3.0 gain_b avg_gray / avg_b gain_g avg_gray / avg_g gain_r avg_gray / avg_r result[:, :, 0] * gain_b result[:, :, 1] * gain_g result[:, :, 2] * gain_r result np.clip(result, 0, 255).astype(np.uint8) return result代码看起来很简单但有几个参数值得说清楚。cv::mean(channels[2])返回的是一个Scalar在单通道Mat上只用到第一个值。convertTo(CV_32FC3)把像素值从0-255的整数变成了浮点数这一步至关重要。最后np.clip的作用是防止乘积结果超过255OpenCV的convertTo本身会做截断但Python版本需要手动处理。我在实际项目里测过灰度世界算法对室内LED光源下的工业零件图像校正效果很不错。但如果是户外自然光下拍的风景或车辆图像效果会略逊于完美反射算法因为户外图像的灰度分布更不均匀。2.3 完美反射算法代码实现及调优细节完美反射算法在OpenCV里实现起来核心是找到图像中最亮的点计算各通道增益。下面是我用的版本在高光明显的场景下效果非常好。void whiteBalancePerfectReflection(cv::Mat src, cv::Mat dst) { src.copyTo(dst); if (dst.channels() ! 3) return; cv::Mat floatImg; dst.convertTo(floatImg, CV_32FC3); std::vectorcv::Mat channels; cv::split(floatImg, channels); // 计算每个像素的亮度取三个通道最大值 cv::Mat maxVal; cv::max(channels[0], channels[1], maxVal); cv::max(maxVal, channels[2], maxVal); // 取所有像素亮度排序后前1%的像素作为参考白点 // 方法计算阈值亮度大于阈值的像素视为白点 std::vectorfloat flatten; flatten.assign((float*)maxVal.datastart, (float*)maxVal.dataend); std::sort(flatten.begin(), flatten.end(), std::greaterfloat()); // 取前1% int topCount std::max(1, (int)(flatten.size() * 0.01)); float thresholdVal flatten[topCount - 1]; // 根据阈值筛选白点像素计算这些区域的各通道均值 cv::Mat mask; cv::threshold(maxVal, mask, thresholdVal, 255, cv::THRESH_BINARY); mask.convertTo(mask, CV_32FC1, 1.0 / 255.0); cv::Scalar sumB cv::sum(channels[0].mul(channels[0].mul(mask))); cv::Scalar sumG cv::sum(channels[1].mul(channels[1].mul(mask))); cv::Scalar sumR cv::sum(channels[2].mul(channels[2].mul(mask))); double count cv::sum(mask)[0]; if (count 1.0) return; double avgB sumB[0] / count; double avgG sumG[0] / count; double avgR sumR[0] / count; // 参考白点的灰度值取三均值最大者 double maxAvg std::max({avgB, avgG, avgR}); channels[0] channels[0] * (maxAvg / avgB); channels[1] channels[1] * (maxAvg / avgG); channels[2] channels[2] * (maxAvg / avgR); cv::merge(channels, dst); dst.convertTo(dst, CV_8UC3); }这段代码里面有一个我一直提醒自己注意的点cv::max在比较三个通道时第二个参数和第三个参数的结果要重新赋给新的Mat不能直接在原值上修改否则会覆盖掉后续需要的通道数据。代码中先比较0和1通道再把结果和2通道比较逻辑是正确且高效的。Python版本类似关键是用np.percentile来快速找亮度阈值比C里手动排序简洁得多。def white_balance_perfect_reflection(img, percent1): result img.astype(np.float32) max_val np.max(result, axis2) threshold np.percentile(max_val, 100 - percent) mask max_val threshold avg_b np.mean(result[:, :, 0][mask]) avg_g np.mean(result[:, :, 1][mask]) avg_r np.mean(result[:, :, 2][mask]) max_avg max(avg_b, avg_g, avg_r) result[:, :, 0] * max_avg / avg_b result[:, :, 1] * max_avg / avg_g result[:, :, 2] * max_avg / avg_r result np.clip(result, 0, 255).astype(np.uint8) return resulttopCount和percent这两个参数是调优的关键。我在不同场景下测试过1%的阈值适用于多数工业图像但如果图像本身有大量高光噪点建议把比例降到0.5%如果图像偏暗又希望校正力度大一点可以提升到3%——但千万不能太高否则把正常的中亮像素当成参考白点会引入色偏。这个参数调起来很直观跑几张图对比一下就能找到感觉。3. Halcon实现方案从另一条路走到同一个终点Halcon实现白平衡的思路和OpenCV有本质区别。OpenCV是在像素值层面直接做数学运算而Halcon更倾向于把图像处理看成是“图像到图像”的算子流。理解这一点你就能明白为什么Halcon的代码看起来更简洁但对操作者的图像处理底层理解要求更高。3.1 Halcon中的彩色图像处理基础Halcon读取一张彩色图像后默认会把图像认为是一个多通道对象。和OpenCV的BGR通道顺序不同Halcon内部使用RGB通道顺序且通道数据可以独立存取也可以整体处理。也就是说decompose3算子可以将一张三通道图像拆成三个独立的单通道图像。Halcon的核心数据结构是HObject它不像OpenCV那样是一个像素矩阵而是一个更抽象的图像对象。白平衡的处理就需要用decompose3拆通道、mean_image求均值、scale_image缩放最后用compose3合并。这种算子式的编程范式优点是每个步骤都清晰可见便于调试和流程审计——在军工、医疗等对可追溯性要求高的行业这种特性非常宝贵。这里顺便说一个问题很多人从OpenCV转向Halcon时容易被通道顺序搞晕。OpenCV里拆出来的channels[0]是BHalcon的decompose3返回的Red是R。同一套算法在不同工具里对应关系要调整。我在项目里犯过这个错误把OpenCV调好的增益参数直接搬到Halcon结果红蓝通道互换校准后的图像依然偏色怎么都调不对最后检查代码才发现是通道顺序的问题。3.2 灰度世界算法的Halcon实现Halcon里写灰度世界算法核心是mean_image和scale_image的组合。下面是标准实现。* 读取图像 read_image (Image, color_image.png) * 拆分成RGB三个通道 decompose3 (Image, R, G, B) * 计算每个通道的均值 mean_image (R, MeanR, 1, 1) mean_image (G, MeanG, 1, 1) mean_image (B, MeanB, 1, 1) * 获取均值像素值 get_grayval_interpolated (MeanR, 0, 0, AvgR) get_grayval_interpolated (MeanG, 0, 0, AvgG) get_grayval_interpolated (MeanB, 0, 0, AvgB) * 计算总均值 MeanGray : (AvgR AvgG AvgB) / 3.0 * 计算增益 GainR : MeanGray / AvgR GainG : MeanGray / AvgG GainB : MeanGray / AvgB * 应用增益注意scale_image的第二个参数是加法第三个参数是乘法 scale_image (R, ScaledR, 0, GainR * 255) scale_image (G, ScaledG, 0, GainG * 255) scale_image (B, ScaledB, 0, GainB * 255) * 合并回彩色图像 compose3 (ScaledR, ScaledG, ScaledB, WhiteBalancedImage)这里有一个很隐蔽的坑scale_image的签名是scale_image(Image, ImageScaled, Mult, Add)实际计算是ImageScaled Image * Mult Add。如果你想把像素扩大1.2倍需要写scale_image(R, ScaledR, 0, 1.2 * 255)而不是scale_image(R, ScaledR, 1.2, 0)因为第三个参数是加数第四个参数才是乘数。更准确地说如果Mult是灰度值映射的偏移Add才是真正的缩放因子。我刚开始用Halcon时每次都被这个参数顺序坑到后来干脆在代码里写注释提醒自己。另一个问题是scale_image在处理浮点结果时输出图像的类型取决于输入图像类型和参数。如果输入是byte类型的R通道scale_image的输出也是byte类型那增益乘完的结果会被截断。这种情况下需要先用convert_image_type把图像转成real类型计算完成后再转回byte。完整版本应该长这样convert_image_type (R, RReal, real) convert_image_type (G, GReal, real) convert_image_type (B, BReal, real) scale_image (RReal, ScaledR, 0, GainR) scale_image (GReal, ScaledG, 0, GainG) scale_image (BReal, ScaledB, 0, GainB) convert_image_type (ScaledR, ScaledRByte, byte) convert_image_type (ScaledG, ScaledGByte, byte) convert_image_type (ScaledB, ScaledBByte, byte) compose3 (ScaledRByte, ScaledGByte, ScaledBByte, WhiteBalancedImage)3.3 完美反射算法的Halcon实现与细节校正完美反射算法在Halcon里实现时难点在于怎么高效地统计亮像素的均值和阈值。Halcon的gray_histo算子可以直接统计灰度直方图配合threshold就能筛选白点。read_image (Image, color_image.png) decompose3 (Image, R, G, B) * 计算每个像素的亮度取RGB最大值 max_image (R, G, MaxRG) max_image (MaxRG, B, MaxRGB) * 统计直方图用来找亮度阈值 gray_histo (MaxRGB, MaxRGB, AbsoluteHisto, RelativeHisto) * 从高灰度端累加找到覆盖前1%像素的阈值 tuple_sum (AbsoluteHisto, TotalPixels) TopCount : int(TotalPixels * 0.01) * 累加高灰度区间的像素数直到达到TopCount Cumulative : 0 ThresholdVal : 255 for i : 255 to 0 by -1 Cumulative : Cumulative AbsoluteHisto[i] if (Cumulative TopCount) ThresholdVal : i break endif endfor * 根据阈值生成掩膜提取白点区域像素均值 threshold (MaxRGB, WhiteMask, ThresholdVal, 255) reduce_domain (R, WhiteMask, RWhite) reduce_domain (G, WhiteMask, GWhite) reduce_domain (B, WhiteMask, BWhite) mean_image (RWhite, MeanRWhite, 1, 1) mean_image (GWhite, MeanGWhite, 1, 1) mean_image (BWhite, MeanBWhite, 1, 1) get_grayval_interpolated (MeanRWhite, 0, 0, AvgRWhite) get_grayval_interpolated (MeanGWhite, 0, 0, AvgGWhite) get_grayval_interpolated (MeanBWhite, 0, 0, AvgBWhite) MaxAvg : max([AvgRWhite, AvgGWhite, AvgBWhite]) * 使用浮点类型避免截断 convert_image_type (R, RReal, real) convert_image_type (G, GReal, real) convert_image_type (B, BReal, real) scale_image (RReal, ScaledR, 0, MaxAvg / AvgRWhite) scale_image (GReal, ScaledG, 0, MaxAvg / AvgGWhite) scale_image (BReal, ScaledB, 0, MaxAvg / AvgBWhite) convert_image_type (ScaledR, ScaledRByte, byte) convert_image_type (ScaledG, ScaledGByte, byte) convert_image_type (ScaledB, ScaledBByte, byte) compose3 (ScaledRByte, ScaledGByte, ScaledBByte, WhiteBalancedImage)这里有几个值得展开的点。gray_histo返回的AbsoluteHisto是一个长度为256的元组AbsoluteHisto[i]表示灰度为i的像素个数。通过从255向下累加找到累加值首次超过TopCount的灰度值这个灰度值就是前1%最亮像素的阈值边界。这个逻辑和OpenCV里np.percentile的目的完全一样但在Halcon中需要手动写循环来模拟因为Halcon没有直接提供相似功能的算子。另外为什么用max_image而不是convert_image_type转到灰度再去排序因为max_image取三个通道最大值能够更好地保留高光信息。如果先转灰度某些RGB分布不均匀但亮度较高的点会被平均掉导致白点检测不准则。Halcon开发时还有一个小技巧在HDevelop里逐步执行代码时可以打开变量窗口查看WhiteMask的区域显示效果。如果掩膜覆盖了画面中大面积的背景区域说明阈值设得太低了应该提高ThresholdVal。反之如果掩膜只有零星几个点说明阈值太高增益计算会不稳定。这两种情况看一眼掩膜图就能判断比反复看输出图像效率高得多。4. 两套方案的实测对比处理效果与性能数据写代码谁都会但实际项目里选哪套方案要看处理效果和运行性能。我在自己测试平台上跑了一组对比实验数据如下。4.1 测试环境与图像样本说明测试平台Windows 10Intel i7-10750H16GB内存。OpenCV版本4.5.2Halcon版本20.11。测试图像是一张在混合光源下拍摄的工业零件照片分辨率1280×1024画面中包含银色金属件、蓝色塑料外壳和白色陶瓷垫片属于典型的偏色样本。偏色情况是整体偏黄蓝色通道明显偏低。样本覆盖三类场景一是上述混合光源图二是纯LED白光下的过曝图主要考验完美反射算法三是模拟黄昏低色温的暖调图主要考验灰度世界算法。所有图像统一为三通道8bit彩色图不用ROI整幅处理。4.2 处理效果对比从实际输出效果来看两个工具在算法正确实现的前提下处理结果基本一致毕竟底层是同一个数学模型。差别主要体现在几个细节上。灰度世界算法在两幅不同色温的样本上表现稳定校正后的色卡白色区域RGB值分别收敛到235238241和232236239接近中性灰但没有完全达到255这是正常现象因为算法没有强制拉伸对比度。完美反射算法在高光样本上表现更好校正后HSL空间的饱和度分布更均匀金属表面的高光细节保留完整。但在暗调样本上由于高光像素太少参考白点的统计量不足导致蓝色通道增益过大图像出现了微弱的紫色色罩。这个现象在OpenCV和Halcon两个实现里都出现了说明是算法本身的问题不是工具的问题。4.3 运行性能对比我记录了两种工具处理单帧图像的平均耗时连续跑100次取均值数据如下实现方案灰度世界耗时毫秒完美反射耗时毫秒OpenCV CDebug18.635.2OpenCV CRelease3.87.1OpenCV Python48.286.4HalconHDevelop环境15.442.7HalconC导出4.59.3从数据能看出几个结论。同一种算法在Release编译的OpenCV和C导出的Halcon性能差距不大完美反射算法耗时明显高于灰度世界主要原因是需要统计直方图和排序。Python版本性能最差但用来做算法原型验证完全够用尤其在快速试错阶段优势明显。Halcon在HDevelop环境跑会额外加载解释器实际工程部署时用C接口性能就上来了。如果只是一次性离线处理图片用Python版本完全没问题开发效率最高。如果是部署到产线实时处理我建议用OpenCV C因为它的依赖管理简单部署机器上装个VC运行库就行Halcon则要额外处理License问题除非客户环境本来就买了Halcon授权。5. 实际项目中的避坑指南通道顺序、类型转换与参数调优到了项目落地阶段真正折磨人的往往不是算法本身而是各种环境细节。下面这几个坑是我在OpenCV和Halcon混用的过程中踩过的每个都值得拉出来单说。5.1 通道顺序混淆问题OpenCV用的是BGR顺序Halcon内部用RGB如果两套代码要做联合调试比如用OpenCV做预处理、Halcon做分析通道顺序就特别容易出错。我踩过最严重的一次是OpenCV里把图像保存成bmp文件Halcon读进来后decompose3得到的Red通道实际上是蓝色通道当时没反应过来结果白平衡的增益都作用反了图像的偏色反而加重了。检查问题时输出三张通道图的灰度值对比才迅速定位到问题。解决方案很简单在代码里加一行注释或者干脆封装一个函数做通道转换。OpenCV中用cv::cvtColor(img, img, cv::COLOR_BGR2RGB)转换Halcon中用transpose_image配合通道重排列但最保险的方法还是确认数据源格式后写死通道映射关系不要依赖工具间自动转换。5.2 数据溢出与截断问题这个问题在第一节提到过但值得再强调一遍。uint8类型的图像像素值范围是0-255白平衡增益普遍在0.8到1.5之间一旦乘完超出了255就会出现截断图像上会形成不自然的色块。尤其在完美反射算法里白色区域的像素值本来就是255附近再乘以大于1的增益所有值都会被截断成255虽然视觉上问题不大但后续如果再叠加其他处理比如对比度增强误差就会累积。我的习惯是凡是涉及像素乘法的算法第一步先把图像转到float或real类型处理最后统一转回8bit。OpenCV用convertTo(CV_32FC3)Halcon用convert_image_type(..., real)。虽然内存占用多了一些但稳定性提高了一大截这个代价完全值得。5.3 参数调优的方向和方法灰度世界算法本身没有需要调的参数它完全依赖统计均值。完美反射算法有两个关键参数一是白点比例percent二是直方图分箱数。第二个参数在Halcon里是固定的256灰度级OpenCV里如果你用calcHist也可以指定但默认256就够用不是调优重点。白点比例直接影响校正幅度。我总结了一个经验公式当图像偏色严重时色温偏差大于2000K白点比例取1%到2%能较好恢复白色但会牺牲部分暗部细节当图像只是轻微偏色时色温偏差小于1000K白点比例建议取0.5%到1%这样校正后色彩更自然不会出现因过度校正导致的色彩断层。多测几张图对比色卡上灰色块的RGB差值是否小于10就能快速判断参数是否合适。5.4 OpenCV和Halcon联合开发时的常见痛点在C#或C项目里同时使用OpenCV和Halcon需要处理图像格式转换。OpenCV的Mat、Halcon的HObject、C#里的Bitmap三种数据格式互转每转一次都有性能损耗。我在一个项目里实际测过一张1280×1024的图像Mat转HObject大约耗时2毫秒频率一高整个流程的耗时占比就很可观。如果工程上允许我建议尽量在单一工具里完成同类型算法。白平衡如果是预处理放在Halcon里和后续的形态学处理串联起来做不要中途切到OpenCV再切回来。我在新项目上的原则是用Halcon做主视觉流程用OpenCV做算法验证和快速原型确认效果后再把算法迁移到Halcon算子里。这样既保留了OpenCV的灵活性又利用了Halcon在工业视觉领域的稳定性和可追溯性。5.5 白平衡效果的评价方式最后说一个容易被忽略的问题怎么客观地评价白平衡效果肉眼看图够不够不够。因为人眼有色彩恒常性看久了会自动适应偏色你会觉得“差不多”但仪器检测可能还差得很远。我的做法是取图像中已知颜色的色块比如色卡上的标准白、标准灰或者产品上的白色标签区域计算处理前后该区域的平均RGB值。校正后R、G、B三个通道的差值绝对值总和越小说明效果越好。比如标准白参考值是255255255处理前是230215248处理后是252250253差值从37降到6这就是一个直观可量化的指标。如果差值的绝对值总和小于10基本可以认为这台相机的白平衡已校准到位可以进入下一道工序了。我在项目交付时都会把这张评价图表处理前和处理后色块的RGB数据对比附在调试报告里客户看得懂后续维护时也有据可查比丢一句“效果已调好”有说服力得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑