You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++实现Bokeh散景模糊亮部无弥散圆,如何调整亮度采样权重?

散景模糊(Bokeh Blur)亮度权重设置问题

我们需要对图像执行Bokeh blur,我测试了如下代码,但无法在亮部点位上得到Circle of Confusion(弥散圆)效果。

void bokeh(unsigned char *Input, unsigned char *Output, int Width, int Height, int Stride, int Radius)
{
    int Channels = Stride / Width;
    int rsq = fmax(1, sqrtf(Radius));
    for (int y = 0; y < Height; y++)
    { 
        unsigned char * LinePD = Output + y*Stride;
        for (int x = 0; x < Width; x++)
        {
            unsigned int sum[3] = { 0 };
            unsigned int weightsum = 0;
            for (int ny = std::max(0, y - Radius); ny < std::min(y + Radius, Height); ny++)
            {
                const    unsigned char * sampleLine = Input + ny*Stride;

                for (int nx = std::max(0, x - Radius); nx < std::min(x + Radius, Width); nx++)
                { 
                    if (sqrtf(nx - x) + sqrtf(ny - y) < rsq)
                    {
                        const    unsigned char * sample = sampleLine + nx*Channels;
                        const    unsigned char&R = sample[0];
                        const    unsigned char&G = sample[1];
                        const    unsigned char&B = sample[2];
                        float weight = sqrtf((unsigned char)((21627 * R + 21627 * G + 21627 * B) >> 16));
                        
                        for (int c = 0; c < Channels; c++)
                        {
                            sum[c] += weight*sample[c];
                        }
                        weightsum += weight;
                    }
                }
            }
            for (int c = 0; c < Channels; c++)
            {
                LinePD[c] = ClampToByte(sum[c] / weightsum);
            } 
            LinePD += Channels;
        }
    }
}

原图如下:
原图
当前运行结果如下:
当前结果
我期望得到如下所示的圆形散景效果:
预期效果
我尝试将sqrtf(nx - x) + sqrtf(ny - y) < rsq
替换为
powf(nx - x, 2.0) + powf(ny - y, 2.0) < powf(Radius, 2)
同时将float weight = sqrtf((unsigned char)((21627 * R + 21627 * G + 21627 * B) >> 16));
替换为
float weight = (R + G + B)*1.0f/3.0f;
后得到了如下的散景模糊效果:
修改后效果
请问如何正确设置基于亮度的采样权重?


修正方案

1. 优化采样区域判断逻辑

原判断逻辑存在计算错误:坐标差可能为负数,开根号会得到NaN,导致采样区域完全不符合预期。你修改后的圆形采样逻辑是对的,可进一步优化性能,避免重复调用高开销的powf函数:

// 外层循环提前计算半径平方
int radius_sq = Radius * Radius;
// 内层采样判断替换为整数平方计算
int dx = nx - x;
int dy = ny - y;
if (dx*dx + dy*dy < radius_sq)

2. 修正亮度计算规则

标准sRGB空间转人眼感知亮度的公式为L = 0.299*R + 0.587*G + 0.114*B,原代码三个通道系数完全相同,不符合亮度感知特性。

3. 正确设置亮度权重

散景效果要求亮部贡献远高于暗部,需要对亮度做非线性放大,拉开亮暗权重差才能得到明显的弥散圆效果。原代码对亮度开根号会压缩亮暗差,属于反向操作,正确写法如下:

// 计算标准亮度,范围0~255
float lum = 0.299f * R + 0.587f * G + 0.114f * B;
// 平方放大亮部权重,需要更突出的散景可换成三次方lum*lum*lum
float weight = lum * lum;

同时注意把sum数组改为float类型,避免unsigned int溢出导致颜色异常。


内容的提问来源于stack exchange,提问作者Chifred

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 03:45:10