Este artigo apresenta a matemática da reconstrução de realces do Ansel : o projeto original de 2021 dos guided laplacians, cujas ideias gerais só tinham sido esboçadas no fórum pixls.us,1 e a transposição harmônica, o método que o substituiu depois que um estudo com verdade de referência (ground truth) expôs um bug de recuperação de magnitude.2 Está organizado de modo que cada leitor encontre sua seção : os resultados e a seção Intuição e contexto de cada método não exigem matemática ; desenvolvedores encontrarão as subseções Implementação e otimizações e o estudo de desempenho ; as conclusões teóricas e o anexo do cemitério foram escritos para serem reutilizáveis fora da fotografia ; e como este trabalho foi de fato realizado documenta o protocolo de pesquisa homem–máquina por trás dele.

Resumo

Quando o sensor de uma câmera satura, os três canais de cor não sofrem clipping ao mesmo tempo, então uma alta luz estourada desvia de cor, geralmente em direção ao magenta. Este artigo documenta a reconstrução de realces do Ansel de ponta a ponta e na ordem em que aconteceu : os guided laplacians originais de 2021 (transferência de detalhes de wavelet através de um filtro guiado, nunca antes registrada por completo), o estudo com verdade de referência que expôs seu bug de magnitude, e o método que o substituiu — a transposição harmônica, que mantém o modelo local de linha de cor do filtro guiado, mas transporta seus coeficientes através da zona estourada como campos suaves difundidos, guiados pela estrutura dos canais sobreviventes, corrige o viés do rolloff de saturação do sensor antes do ajuste, e entrega cada banda de frequência e classe de pixel ao estimador que é comprovadamente o melhor ali. Cada decisão de projeto é respaldada por um número em um banco de testes público, e as falhas são documentadas com o mesmo cuidado que os sucessos, incluindo dois resultados que acreditamos serem gerais : a energia de emenda em qualquer transição entre estimadores é igual à discordância dos estimadores e não pode ser eliminada por ponderação, e a validade de uma linha de cor bem no interior de uma zona estourada é indecidível a partir dos dados ao seu redor.34

O problema

A dominante magenta

Um sensor digital é uma matriz de fotossítios, cada um coberto por um filtro colorido de uma matriz de filtros de cor (CFA) : o padrão Bayer (2×2 de R, G, G, B) ou o X-Trans da Fuji (6×6). Cada fotossítio é um poço de potencial que se enche de fotoelétrons durante a exposição e satura a uma capacidade fixa. Como a capacidade do poço é uma propriedade do silício, todas as três cores saturam mais ou menos no mesmo valor de código raw.

A armadilha é o balanço de branco. Um objeto cinza neutro não produz sinais raw iguais nos três canais: as transmissões da CFA, a sensibilidade espectral do sensor e o iluminante da cena diferem todos por canal. Para renderizar tal objeto como neutro, o revelador raw multiplica cada canal por um coeficiente de balanço de branco : normalmente o canal verde é deixado próximo de $1$ enquanto o vermelho e o azul são multiplicados por $1.5$ a $2$.

Agora acompanhe uma alta luz neutra à medida que fica mais brilhante. No sensor não há nada por canal na saturação: os três sinais raw sobem em direção a um teto compartilhado — o poço cheio — e sofrem clipping em mais ou menos o mesmo valor. O magenta é fabricado depois, pelos ganhos de balanço de branco: multiplicar cada canal altera tanto sua inclinação na subida quanto o nível em que seu platô recortado se estabelece. O verde (ganho $\approx 1$) atinge o platô onde o sensor o deixou ; o azul e o vermelho são empurrados para cima por seus ganhos e atingem o platô acima dele. Passada a saturação, as razões registradas não são mais neutras — vermelho e azul ultrapassam o verde, e a alta luz “branca” se lê como magenta. (Em objetos reais os canais ainda sofrem clipping em sequência — a própria cor do objeto e as sensibilidades por canal escalonam os inícios, e é por isso que existem pixels parcialmente recortados que carregam guias sobreviventes — mas o magenta de um neutro estourado é obra dos ganhos, não de limiares por canal.)

Esquerda : no sensor, uma alta luz neutra leva os três canais em direção ao mesmo teto — o poço cheio — e eles sofrem clipping juntos. Direita : os ganhos de balanço de branco (aqui R × 2.0, B × 1.5, G × 1.0) alteram tanto a inclinação quanto a altura de cada platô recortado ; vermelho e azul se estabelecem acima do verde, e a cor registrada desvia para magenta — mesmo que o objeto seja cinza e o sensor tenha recortado todos os canais no mesmo nível.

O valor em que um canal é declarado recortado não é o máximo numérico, mas um limiar por canal derivado do ponto branco raw:

$$ \text{clip}_c = 0.995 \times \texttt{clip} \times \text{white}_c, $$

onde $\text{white}_c$ é o processed_maximum do módulo para o canal $c$ (o nível de branco raw por canal que sobreviveu aos estágios anteriores do pipeline) e clip é um fator de segurança do usuário em torno de $1$. A margem de $0.995$ mantém fotossítios quase saturados (cuja resposta já se tornou não linear perto do topo do poço) fora do conjunto “válido”.2

Correções mais simples

O módulo do Ansel oferece três modos de reconstrução mais baratos antes do guided-laplacian, e vale a pena enunciá-los porque enquadram o que o método caro proporciona:

  • Clip simplesmente esmaga todo canal até o limiar comum $\texttt{clip}\times\min_c \text{white}_c$ (os mesmos níveis de branco, sem a margem de $0.995$). Sem magenta, mas toda região recortada vira uma mancha branca plana e sem textura.
  • Reconstruir em LCh converte cada bloco Bayer em um trio luminância/croma/matiz, reescala o croma dos blocos recortados para casar com a luminância não recortada, e converte de volta. Remove o desvio de matiz mas não consegue inventar textura.2
  • Reconstruir cor (inpaint) propaga razões de cor a partir de pixels vizinhos não recortados ao longo de linhas e colunas, usando a atualização de razão por decaimento exponencial do algoritmo do Magic Lantern. É rápido e direcional mas unidimensional e facilmente enganado por bordas complexas.2

O modo guided laplacians é o único que restaura tanto a textura quanto a magnitude de uma região recortada, tomando emprestado dos canais que de fato sobreviveram ao longo da linha de cor local.

O Darktable a montante inclui dois modos de reconstrução que o Ansel não carrega, e eles oferecem uma comparação instrutiva porque partem de uma suposição diferente sobre o que é uma alta luz estourada. Ambos foram desenvolvidos pelas equipes do G’mic e do Darktable e ambos trabalham, como nosso método, sobre o mosaico raw : cada canal de cor é primeiro aproximado em toda parte a partir de sua vizinhança de fotossítios 3×3, dando uma imagem por canal grosseira mas de resolução plena.

O Inpaint opposed repousa sobre uma observação empírica : para um canal recortado, a média dos dois outros canais (a média “oposta”, calculada no espaço de raiz cúbica para comprimir a faixa dinâmica) é uma boa estimativa do valor faltante na vasta maioria das imagens. O mecanismo é, portanto, simples : substituir todo valor recortado por essa média oposta, e então adicionar um deslocamento de crominância global, medido nos pixels morfologicamente mais próximos das áreas recortadas, para absorver a dominante geral de cor. Seus modos de falha documentados são as cenas em que uma relação fixa não pode se sustentar : iluminação mista, balanço de branco distante da suposição do pipeline, ou várias fontes de luz com cores diferentes alimentando diferentes realces.

O Segmentation based refina o mesmo estimador base com adaptação espacial. Os pixels recortados de cada canal são preenchidos por inundação em segmentos conexos (opcionalmente mesclados por um fechamento morfológico, o controle “combine”) ; para cada segmento, o algoritmo busca em sua borda não recortada o melhor pixel candidato (pontuado pelo desvio padrão local e pela mediana em uma janela 5×5) e transplanta a pseudo-crominância desse candidato (o canal menos a média oposta, novamente no espaço de raiz cúbica) por todo o segmento. Se nenhum candidato convincente existir, ele recorre a uma média por todo o segmento com uma correção de crominância. Onde todos os canais sofrem clipping, um passo separado de “rebuild” extrapola uma superfície de luminância a partir dos gradientes da borda, controlado por uma transformada de distância — o parente mais próximo, em toda esta paisagem, do nosso domo biharmônico. As suposições são, portanto : uma crominância representativa por segmento (melhor do que uma por imagem, mas ainda plana dentro de um segmento), textura herdada do que quer que os canais sobreviventes carreguem, e extrapolação de gradiente onde nada sobreviveu.

Ambos são tentativas desesperadas de preencher a área danificada com algo plausível, mas promovem superfícies coloridas planas na magnitude errada.

Primeiros princípios

O método é uma montagem de quatro ideias. Duas delas (Laplacianos discretos e a pirâmide B-spline à-trous) são compartilhadas literalmente com diffuse or sharpen e são apenas resumidas aqui. As outras duas, o filtro guiado e a difusão de crominância, carregam a reconstrução e são derivadas por completo.

Gradientes e Laplacianos

Para uma imagem discreta $u(i,j)$, o gradiente mede a inclinação local,

$$ \nabla u = \left( \frac{u(i+1,j) - u(i-1,j)}{2}, \; \frac{u(i,j+1) - u(i,j-1)}{2} \right), $$

e o Laplaciano mede a curvatura local : o quanto um pixel se afasta da média de seus vizinhos,

$$ \Delta u = \frac{\partial^2 u}{\partial x^2} + \frac{\partial^2 u}{\partial y^2}. $$

O Laplaciano é o cavalo de batalha aqui porque isola a textura como oscilação em torno de uma média local (é zero em regiões planas e responde apenas ao contraste local) e porque é linear: superexpor ou subexpor a imagem simplesmente o reescala (uma propriedade em que nos apoiamos abaixo). Isolar a textura desta forma é o que nos permite transplantá-la entre canais sem arrastar junto o brilho absoluto do guia ; a diferença de magnitude geral entre um canal recortado e seu guia é absorvida pela inclinação do filtro guiado, não pelo próprio Laplaciano. O Ansel usa o estêncil de 9 pontos rotacionalmente simétrico de Oono & Puri

$$ \mathbf{K}_{\text{iso}} = \begin{bmatrix} \tfrac14 & \tfrac12 & \tfrac14 \\ \tfrac12 & -3 & \tfrac12 \\ \tfrac14 & \tfrac12 & \tfrac14 \end{bmatrix}, $$

cujo erro angular é muito menor do que o da ingênua cruz de 5 pontos, de modo que a difusão não privilegia os eixos da grade de pixels.567

A pirâmide de B-spline à-trous

Para agir sobre estruturas de muitos tamanhos, a imagem é dividida em bandas de frequência borrando-a repetidamente com o kernel B-spline cardinal separável

$$ h_0 = \frac{1}{16}[1,4,6,4,1], $$

uma aproximação compacta de uma Gaussiana de parâmetro $\sigma_B \approx 1.0554$.8 Na escala $s$ os taps são espaçados por um passo de $2^s$ pixels (“à-trous” = “com buracos”), de modo que o mesmo kernel minúsculo alcança cada vez mais longe sem nunca crescer em custo. Escrevendo $G_s$ para as sucessivas imagens passa-baixa (progressivamente borradas) e $H_s$ para as bandas de detalhe,

$$ G_{-1} = u, \qquad G_s = h_{2^s} * G_{s-1}, \qquad H_s = G_{s-1} - G_s, $$

a imagem é exatamente a soma de suas bandas, $u = \sum_{s=0}^{n-1} H_s + G_{n-1}$. Uma banda de detalhe $H_s$ é uma diferença de Gaussianas, que é ela mesma uma aproximação escalonada de um Laplaciano-de-Gaussiana — de modo que “filtrar a banda $H_s$” e “aplicar um Laplaciano na escala $s$” são duas visões da mesma operação. A derivação completa, incluindo como o raio Gaussiano equivalente cresce como

$$ \sigma_{G,s} = \sigma_B \sqrt{\frac{4^{s+1}-1}{3}}, $$

é apresentada no artigo complementar sobre diffuse or sharpen.29

O filtro guiado

O filtro guiado de He, Sun e Tang é o motor que toma emprestada textura de um bom canal para um recortado.3 Suponha que queiramos produzir uma imagem de saída $q$ que permaneça fiel a algum alvo $p$ mas ostente as bordas e a textura de um guia $I$. Suponha que, dentro de qualquer pequena janela $\omega_k$ em torno do pixel $k$, a saída seja uma função afim do guia:

$$ q_i = a_k \, I_i + b_k, \qquad \forall i \in \omega_k. $$

Esta única suposição, uma linha de cor local, é todo o modelo. Ela diz que dentro de um pequeno trecho o canal que estamos reconstruindo é apenas uma cópia escalonada e deslocada do guia. É o mesmo a priori que fundamenta a interpolação cromática entre canais, a remoção de neblina, o matting de imagem e a colorização: superfícies naturais traçam linhas de cor (localmente, seus canais estão relacionados afimente) porque a maioria das bordas são mudanças de refletância que escalam todos os canais juntos.10 Sob um mapeamento afim, $\nabla q = a_k \nabla I$, de modo que $q$ herda cada borda de $I$, meramente reescalada por $a_k$.

Ajustamos $(a_k, b_k)$ por mínimos quadrados, mantendo $a_k$ pequeno para evitar amplificar ruído (um termo de regularização ridge $\varepsilon a_k^2$):

$$ E(a_k, b_k) = \sum_{i \in \omega_k} \Big[ (a_k I_i + b_k - p_i)^2 + \varepsilon \, a_k^2 \Big]. $$

Igualar as derivadas a zero dá a forma fechada que aparece, quase literalmente, no código:

$$ \begin{aligned} a_k &= \frac{\operatorname{cov}_{\omega_k}(I, p)}{\operatorname{var}_{\omega_k}(I) + \varepsilon}, \\ b_k &= \bar{p}_{\omega_k} - a_k \, \bar{I}_{\omega_k}. \end{aligned} $$

A covariância no numerador (uma medida do quanto guia e alvo sobem e descem juntos) é a chave: onde se movem juntos, $a_k \to 1$ e a textura do guia é copiada ; onde o guia é plano ($\operatorname{var} \to 0$), $a_k \to 0$ e a saída recai sobre a média local $\bar p$. O parâmetro de regularização ridge $\varepsilon$ define a escala abaixo da qual as variações são tratadas como ruído e suavizadas em vez de transferidas.

A difusão como inpainting de cor

Preencher a cor de um buraco é um problema diferente de preencher sua textura. A cor de um buraco deve variar suavemente e casar com sua borda ; ela não deve carregar detalhe de alta frequência (textura fina, de variação rápida) próprio. O formalismo natural é a energia de Dirichlet, a quantidade total de variação que a imagem contém, grande onde ela oscila e zero onde é constante :

$$ E[u] = \int_\Omega \lVert \nabla u \rVert^2 \, \mathrm{d}\Omega, $$

cujo minimizador sobre o buraco $\Omega$, com os pixels ao redor como condição de contorno, é a função harmônica que satisfaz $\Delta u = 0$.

(Notação, usada ao longo do artigo : $\lVert v \rVert$ é a norma euclidiana de um vetor — para o gradiente, $\lVert \nabla u \rVert = \sqrt{(\partial_x u)^2 + (\partial_y u)^2}$, a magnitude da inclinação local ; $\lvert s \rvert$ é o valor absoluto de um escalar ; e $\langle \cdot \rangle$ — colchetes angulares — é a média da quantidade contida sobre os pixels da região.) A descida de gradiente desta energia é precisamente a equação do calor

$$ \frac{\partial u}{\partial t} = \Delta u, $$

isto é, difusão isotrópica, isotrópica significando cega à direção, espalhando-se igualmente em todas as direções. Executá-la espalha a cor do contorno para dentro até que o buraco seja preenchido por uma superfície suave, sem curvatura. Este é o mesmo modelo de inpainting por transferência de calor anisotrópica de Qin et al. que o Ansel já usa para diffuse or sharpen, restrito aqui a seu caso isotrópico.4 Vamos aplicá-lo não aos pixels mas às razões de cor, de modo que apenas a crominância seja suavizada enquanto a luminância reconstruída é deixada intacta.

Direcionando a difusão : a extensão anisotrópica. A energia de Dirichlet acima trata cada direção igualmente ; ela é isotrópica. Ela se generaliza para uma forma ponderada,

$$ E(u) = \int_\Omega \nabla u^\top D \,\nabla u \,\mathrm{d}\Omega , $$

onde o tensor de difusão $D$ é uma pequena matriz simétrica definida em cada pixel cuja função é reponderar direções : condutância plena ao longo de um de seus autovetores, condutância amortecida ao longo do outro. Minimizar esta energia resolve $\mathrm{div}(D\,\nabla u) = 0$ (sua equação de Euler–Lagrange), e com $D$ igual à identidade em toda parte ela se reduz exatamente ao caso isotrópico acima. O sentido dessa maquinaria é o direcionamento : escolher a direção forte ao longo das isófotas da imagem (as linhas de brilho constante) e a amortecida transversalmente a elas, e a difusão suaviza uma quantidade ao longo da estrutura da imagem enquanto se recusa a carregá-la através das bordas.

Discretizar isto com segurança exige uma precaução. Escrevendo o tensor $D = \begin{pmatrix} a & b \ b & c \end{pmatrix}$ (com média entre cada pixel e seu vizinho, com o termo cruzado limitado a $|b| \leq \min(a, c)$), a divergência $\operatorname{div}(D\,\nabla u)$ torna-se uma soma sobre os oito vizinhos com pesos

$$ w_{\pm e_x} = a - |b|, \qquad w_{\pm e_y} = c - |b|, \qquad w_{\pm(e_x + e_y)} = \max(b,\, 0), \qquad w_{\pm(e_x - e_y)} = \max(-b,\, 0), $$

o estêncil preservador de não negatividade de Weickert : a limitação garante que todo peso é não negativo, de modo que a relaxação ancorada $u_0 \leftarrow \sum_k w_k u_k / \sum_k w_k$ substitui cada incógnita por uma combinação convexa de seus vizinhos. Este é o princípio do máximo discreto — os valores resolvidos nunca podem sair do intervalo das âncoras, não importa quão fortemente o tensor direcione — e é o que torna a difusão anisotrópica segura de executar sobre dados que devem permanecer físicos. O método distribuído usa esta maquinaria em dois lugares : o transporte dos coeficientes da linha de cor através da zona estourada (passo 3 do algoritmo, onde o tensor adicionalmente mescla entre o direcionamento por isófota e por gradiente por meio de uma probabilidade de borda medida), e a crominância dos núcleos totalmente recortados (passo 8), onde as cores devem seguir a estrutura da luminância recuperada e nunca vazar através dela.

Inpainting biharmônico

O inpainting harmônico é a ferramenta certa para um sinal que deveria ficar plano dentro do buraco : uma crominância suave. É a ferramenta errada para um que ainda estava subindo quando o sensor o recortou: a magnitude de uma alta luz estourada continuou subindo, e preenchê-la de forma plana ($\Delta u = 0$) deixa um disco fosco onde deveria haver um domo brilhante. Para carregar a inclinação ao redor para dentro em vez de apagá-la, penalize a flexão do sinal em vez de seu gradiente : minimize a energia de placa fina (biharmônica)

$$ E_{\text{bihar}}[u] = \int_\Omega (\Delta u)^2 \, \mathrm{d}\Omega \qquad \Longrightarrow \qquad \Delta^2 u = 0 \ \text{ on } \Omega, \quad u\big|_{\partial\Omega} = u_{\text{valid}} , $$

cuja equação de Euler–Lagrange (a condição que qualquer minimizador da energia deve satisfazer) é a equação biharmônica $\Delta^2 u = 0$. Onde a solução harmônica força $\Delta u = 0$ (uma superfície mínima plana), a solução biharmônica torna $\Delta u$ ele mesmo harmônico : a curvatura na borda é carregada para o interior, de modo que o gradiente ascendente do contorno é extrapolado em um domo (uma spline de placa fina). É a contraparte de ordem superior, extensora de gradiente, da difusão, no mesmo espírito em que diffuse or sharpen inverte o sinal do Laplaciano para aguçar em vez de suavizar. Nós a resolvemos como um sistema linear esparso direto sobre o buraco, e a usamos abaixo para reconstruir a magnitude de um canal recortado a partir de sua própria borda válida onde quer que nenhum canal correlacionado sobreviva para guiá-la.7

O método de 2021 : guided laplacians

Antes de acompanhar a evolução do método, esta seção documenta o projeto original, tal como foi distribuído em 2021 e como ainda é distribuído hoje — projeto inalterado, embora compartilhe uma correção medida de coleta de borda com o novo pipeline (documentada com os refinamentos harmônicos abaixo) — sob o modo guided laplacians (o novo método é um modo separado, opcional : veja a nota de status mais adiante). Foi codificado por intuição e ajustado a olho em imagens naturais, e suas ideias gerais só tinham sido esboçadas no fórum pixls.us1 ; este é seu primeiro registro completo.

Intuição e contexto

A intuição. O clipping inevitável acontece dentro de fontes de luz : o sol, uma chama, um reflexo especular. Tais fontes são cercadas por um halo brilhante, e esse halo carrega três tipos de informação exploráveis. Primeiro, os canais de cor são fortemente correlacionados ali (todos sobem em direção à fonte), de modo que um canal que sofreu clipping pode tomar emprestada a forma de um canal que não sofreu. Segundo, os gradientes do halo apontam para a fonte, de modo que mesmo um núcleo totalmente estourado pode ser continuado plausivelmente propagando os gradientes ao redor para dentro. Terceiro, o halo é colorido como a própria fonte de luz, de modo que a crominância pode ser recuperada propagando razões RGB para dentro. O projeto de 2021 transformou cada intuição em um operador : uma transferência guiada de estrutura fina entre canais, e uma difusão iterada que empurra estrutura do entorno válido para dentro da região estourada enquanto difunde razões de cor para dentro.

Os objetivos. Reconstruir antes da interpolação cromática, sobre o mosaico raw, de modo que a interpolação nunca veja amostras recortadas ; nunca depender do balanço de branco ou de qualquer suposição sobre qual cor a alta luz “deveria” ter (a própria cor da fonte de luz é incognoscível) ; e preferir continuações suaves e fisicamente plausíveis a alucinações abruptas, porque um erro numa alta luz estourada é muito mais visível como um artefato duro do que como um suave.

Seu histórico. Em seus casos de projeto o método funcionava, e ainda funciona : um disco solar ou um especular na água é reconstruído com estrutura crível e sem o retalho cinza plano que o clipping simples deixa. Ele preserva gradientes onde métodos mais simples (escalonamento de razão de canais, inpainting de cor única) os achatam, e como trabalha sobre gradientes em vez de cores, tolera balanço de branco errado. Dois limites estruturais foram compreendidos e aceitos desde o início : um céu estourado visto através de folhas verdes é recuperado verde (o método propaga a cor vizinha, por projeto), e aberração cromática forte quebra o alinhamento de canais de que a linha de cor precisa.

O problema de otimização

A transferência de textura. A imagem é decomposta com a pirâmide B-spline à-trous descrita acima : em cada escala $s$ o sinal se divide em uma aproximação de baixa frequência e uma banda de detalhe $H_s$. Como a B-spline cardinal é muito próxima de uma Gaussiana, e a banda de detalhe é calculada por escala como a diferença com essa Gaussiana aproximada, $H_s$ é ela mesma já próxima de um Laplaciano, reescalada em magnitude (na verdade ela superavalia o Laplaciano). Em cada banda de detalhe, cada canal recortado $c$ é ajustado contra um canal guia $g$ (escolhido por pixel como o canal de maior variância local, isto é, o que carrega mais estrutura — sem verificação de que o próprio guia seja válido ali) com o filtro guiado derivado acima :

$$ H_c \;\leftarrow\; a_c\, H_g + b_c, \qquad a_c = \frac{\operatorname{cov}(H_c, H_g)}{\operatorname{var}(H_g)}, $$

onde a covariância e a variância são estatísticas locais por janela (uma caixa dura 3×3 que não exclui amostras recortadas), e a divisão é simplesmente pulada onde a variância do guia cai abaixo de $10^{-12}$ — não há termo de amortecimento. Como uma banda de detalhe tem média zero, o intercepto $b_c$ é essencialmente zero : a transferência move textura do guia para o canal recortado, escalonada por sua relação local medida.

A difusão de crominância. O segundo estágio opera sobre as razões RGB (a crominância, $\text{RGB}/n$ com $n = \lVert \text{RGB} \rVert$, a norma euclidiana — o símbolo que as regras de atualização abaixo usam), iterando o Laplaciano de nove pontos de Oono & Puri em cada plano de razão ; o plano da norma $n$ percorre a mesma pirâmide mas é explicitamente protegido do passo (salvo e restaurado em torno dele), de modo que é ressintetizado como está. Essa iteração é a descida de gradiente de um problema variacional explícito : sobre a região recortada $\Omega$, com a borda válida como dado de contorno, ela minimiza

$$ E_{\text{2021}}[u] \;=\; \int_{\Omega} \lVert \nabla u \rVert^2 \,\mathrm{d}\Omega \;+\; \lambda_{\text{solid}} \int_{\Omega} \lVert u - \bar u \rVert^2 \,\mathrm{d}\Omega , \qquad u\big|_{\partial\Omega} = u_{\text{valid}}, $$

para cada plano de razão e para a norma : uma energia de Dirichlet (harmônica) que carrega a crominância e o nível da borda suavemente para dentro, mais um termo de blindagem opcional puxando o núcleo em direção à cor válida média $\bar u$ com o peso “inpaint uma cor plana” do usuário $\lambda_{\text{solid}}$. Sua equação de Euler–Lagrange é a equação de Poisson blindada da seção teórica ; o código de 2021 nunca a resolve até o estado estacionário — o controle deslizante de “iterations” trunca a descida, de modo que o resultado prático depende tanto do orçamento de iterações quanto da energia. Duas propriedades decorrem diretamente deste objetivo e explicam a assinatura visual do método : um preenchimento harmônico achata (ele penaliza gradientes, de modo que o interior tende a um platô nivelado — a seção teórica mostra por que uma energia biharmônica é necessária para continuar inclinações), e nada em nenhum dos estágios restringe o nível reconstruído de um canal recortado, que o estágio 1 não consegue transferir (bandas de média zero) e o estágio 2 apenas difunde a partir de uma borda que se situa no clip.

O algoritmo

O procedimento inteiro, num relance :

flowchart TD
  A["raw mosaic (one colour per pixel)"] --> B["bilinear interpolation to temporary RGB
+ per-channel clip masks"] B --> C["downsample 4× (for speed)"] C --> D["à-trous B-spline pyramid:
split into detail bands, one per frequency range"] D --> E["per band, RGB, fine → coarse:
guided-filter fit of the clipped channel
against the most textured channel
(validity not checked)
→ transfer of TEXTURE (zero-mean details)"] E --> F["per band, RGB ratios & norm, iterated 9-point Laplacian diffusion:
spread structure from the valid rim
into the blown core, isotropically"] F --> G["optional: reaction toward a flat colour
(user slider) + Poissonian re-graining"] G --> H["upsample, remosaic,
feathered composite over the raw"]

As compensações são deliberadas. Trabalhar sobre bandas de detalhe torna a transferência imune a erros de balanço de branco (apenas formas se movem entre canais). O Laplaciano de nove pontos é a discretização isotrópica, invariante à rotação, derivada em primeiros princípios, de modo que a difusão preenche o núcleo de fora para dentro sem seguir os eixos da grade de pixels. A suavidade é sempre preferida à nitidez : um erro numa alta luz estourada se lê como um artefato duro quando nítido e como um brilho plausível quando suave. E a ordem de bandas do fino ao grosso significa que cada banda é ajustada independentemente dentro de um único passo — não há restrição de consistência entre escalas, o que permanece inofensivo precisamente porque as bandas têm média zero.

Três estágios preparatórios merecem uma nota — são compartilhados por todo método de reconstrução deste artigo, o sucessor incluído.

Interpolação cromática bilinear. O mosaico é demosaicado bilinearmente para uma imagem RGB temporária : uma interpolação cromática descartável cujo único trabalho é dar a cada canal um valor em toda parte para o ajuste guiado por canal ; a saída final é remosaicada de volta a um único canal. Uma máscara de clip por canal, junto com seu OU lógico (suavizado na opacidade de composição $\alpha$), registra quais canais saturaram.2

Normalização local de canais. Cada cor é dividida pelo valor médio dessa cor no ladrilho atual, um balanço de branco local grosseiro calculado na hora. Isso equaliza as magnitudes dos canais de modo que a comparação de variância da seleção de guia não seja enviesada em favor de qualquer canal que carregue os maiores números raw ; ele deliberadamente não reutiliza o balanço de branco declarado a montante.2

Suavização da máscara. A máscara binária é suavizada por uma pequena média de caixa $5\times5$ em uma opacidade $\alpha \in [0,1]$, usada como o peso final de composição e como os pesos suaves por canal da reconstrução à-trous abaixo. A suavização amacia a emenda onde a reconstrução encontra pixels intocados ; nos testes numéricos ela reduziu o erro de fronteira deste método em vez de aumentá-lo — removê-la foi testado e prejudica de forma mensurável aqui (os pesos suaves são estruturais), enquanto o método sucessor abandona a suavização por completo (suas máscaras são binárias de ponta a ponta ; veja o cemitério).

As regras de atualização

O problema de otimização enuncia o que este método minimiza e o algoritmo narra seus estágios ; aqui estão as atualizações de sinal de fato do modo de 2021 distribuído, em ordem de execução, para um implementador começando do zero. Planos : o RGB temporário reduzido por um fator de quatro $u_c$ (interpolação cromática bilinear, normalizado por canal), as máscaras suavizadas por canal $\alpha_c \in [0,1]$ e sua opacidade de qualquer-clip $\alpha$. Decomposição : a pirâmide B-spline à-trous com passo $2^s$ por escala — borrões em cascata $\mathrm{LF}_s$, bandas de detalhe $D_s = \mathrm{LF}_{s-1} - \mathrm{LF}_s$, mais o resíduo mais grosseiro.

1. Transferência de textura (o passo RGB), por escala do fino ao grosso, em cada pixel onde $\alpha > 0$. Na vizinhança à-trous $3\times3$ (passo $2^s$) da banda de detalhe, calcule a média e a variância do trecho por canal, escolha o guia $g$ como o canal de maior variância do trecho (a validade não é verificada — a raiz da falha 4), ajuste a linha de cor com viés de intercepto zero nos detalhes,

$$ a_c = \max\!\left( \frac{\operatorname{cov}(D_g, D_c)}{\operatorname{var}(D_g)},\, 0 \right), \qquad b_c = \bar D_c - a_c\, \bar D_g, $$

e mescle a predição, desvanecendo quadraticamente com o raio equivalente da escala — tomado no passo $4s$, não $s$ : o código avalia $\sigma$ em s * DS_FACTOR, cobrando de cada escala também o fator de redução, de modo que o desvanecimento é muito mais íngreme do que $\sigma_s^2$ sozinho sugere ($\beta$ é $\alpha/380$ em $s{=}1$ e $\alpha/97\,000$ em $s{=}2$ — a transferência guiada efetivamente vive nas escalas mais finas) :

$$ D_c \;\leftarrow\; \beta_c \,\big(a_c\, D_g + b_c\big) + (1 - \beta_c)\, D_c, \qquad \beta_c = \frac{\alpha_c}{\sigma_{G,\,4s}^{2}}. $$

A ressíntese soma as bandas processadas mais o resíduo, limitado a $\geq 0$ ; na última iteração, grão poissoniano de amplitude $\sigma = u_c \cdot \texttt{noise_level}$ é incorporado (apenas clareando) sob $\alpha$. O resultado é dividido em direção e magnitude, $r_c = u_c / \lVert u \rVert$ e $n = \lVert u \rVert$, para o próximo passo.

2. Difusão de crominância (o passo de croma), mesma pirâmide sobre os quatro planos $(r_R, r_G, r_B, n)$ : cada banda de detalhe dos três planos de razão dá um passo de Euler explícito da equação do calor blindada, por pixel onde $\alpha > 0$ e por canal — a banda do plano da norma é salva antes do passo e restaurada depois dele, de modo que $n$ atravessa a pirâmide sem difusão,

$$ D \;\leftarrow\; D + \alpha_c\, \kappa \,\big( \mathbf{K}_{\text{iso}} \!*\! D \;-\; \lambda\, D \big), $$

com $\mathbf{K}_{\text{iso}}$ o Laplaciano isotrópico de nove pontos de primeiros princípios, $\kappa = \sigma_B^2 / (2\sqrt{\pi}) \approx 0.31$ o reescalonamento banda-para-Laplaciano, e $\lambda$ a reação de cor plana (solid_color). Ressíntese como acima, então as razões são renormalizadas para norma unitária e recombinadas, $u_c \leftarrow r_c \cdot n$.

3. Iterar. Os passos 1–2 repetem iterations vezes (o controle deslizante do usuário) ; nada detecta convergência, a contagem é o orçamento.

4. Compor. A reconstrução é ampliada quatro vezes e mesclada sobre o mosaico raw através da opacidade suavizada, por fotossítio de cor $c$ : $\text{out} = \alpha\, u_c + (1 - \alpha)\, \text{raw}$.

Nada mais escreve um pixel — e, como o estudo com verdade de referência descobriu, nada acima transfere um nível : cada atualização age sobre bandas de detalhe de média zero ou sobre razões, que é exatamente a lacuna estrutural que o sucessor fecha.

Implementação e otimizações

A reconstrução inteira roda sobre um buffer reduzido por um fator de quatro — um dezesseis avos dos pixels — e o resultado é ampliado e composto de volta sobre o raw sob uma máscara suavizada. Essa única decisão proporciona a maior parte da velocidade do método e custa precisão mensurável : reexecutar a mesma matemática em resolução plena reduz o erro de fronteira em cerca de 30 %, porque a reconstrução ampliada fica borrada contra o original nítido ao qual deve se juntar (o método sucessor roda em resolução plena exatamente por esse motivo). As bandas de detalhe à-trous superavaliam o Laplaciano por uma constante conhecida (o $\sigma_B = 1.05537$ da B-spline dá $1/\kappa = 3.1827$), que o passo de difusão compensa ; a difusão roda iterations × (um passo de razão RGB + um passo de norma) por escala, sobre o quadro inteiro — não há segmentação de região, de modo que o custo escala com a imagem, não com a área recortada. Grão poissoniano ($\sigma = \text{valor} \times \text{nível de ruído}$) é regenerado na última iteração para que áreas reconstruídas não pareçam plasticamente lisas ao lado de textura real.

Problemas descobertos

O que nunca acontece no fluxograma acima é uma transferência de nível — uma observação invisível sem verdade de referência, e onde começa todo o estudo de 2026.

As questões. Na prática, usuários continuavam relatando realces estouradas que permaneciam magenta após a reconstrução — o exato defeito que o método existe para corrigir. O estudo documentado no restante deste artigo acabou localizando a causa raiz na matemática acima : o ajuste guiado era aplicado apenas às bandas de detalhe. Uma banda de detalhe tem média local zero, de modo que o intercepto $b_c$ não carrega energia, e apenas textura é transferida — nunca o nível. A componente de baixa frequência do canal recortado, fixada no valor de clip, era readicionada inalterada ; um canal estourado era deixado no clip (ou empurrado abaixo dele pelo tratamento de crominância) e permanecia magenta. No banco de validação construído posteriormente, esta reconstrução original é frequentemente pior do que não fazer nada em clips de canal único : um erro quadrático médio (RMSE) de 0.073 contra 0.049 por deixar os pixels recortados, em uma cena semelhante à natural onde a reconstrução eventual atinge 0.015 (estes três números vêm do banco do protótipo de pesquisa em Python — fix_prototype.py no repositório de pesquisa  — cujo port em NumPy do módulo de 2021 faz as vezes do C ; as implementações distribuídas são pontuadas em os resultados).

image

Uma cena sintética: três realces saturadas de canal único (discos vermelho, verde, azul) e uma quase neutra brilhante. Ground truth é a cena não recortada, clipped o que o sensor registra, previous method o port em Python do módulo à-trous de 2021, corrected method o mesmo projeto com o reparo de sinal completo descrito logo abaixo.

Nos discos de canal único o resultado corrigido sobe de volta em direção à cor verdadeira (por exemplo, o disco vermelho recupera $\approx 1.7$ vs. verdadeiro $\approx 1.9$, com verde e azul casados exatamente) onde o método anterior fica abaixo do clip ($\approx 0.9$). O disco neutro todo recortado, plano no clip tanto no sensor quanto no método anterior, é re-abobadado pelo preenchimento de luminância compartilhado (centro $\approx 1.5$ vs. verdadeiro $\approx 1.6$) e permanece quase neutro, sua crominância carregada pela difusão da borda.

O reparo de uma linha — aplicar o mesmo ajuste guiado ao sinal completo (baixa frequência e detalhe juntos) de modo que o intercepto carregue a média local e um canal estourado possa subir acima do clip (a matemática está em as regras de atualização) — corrige essa classe de falhas, e é a base que tudo depois manteve : todos os métodos subsequentes deste artigo ajustam linhas de cor sobre valores completos. Foi necessário mas não suficiente. Com verdade de referência para medir — no projeto reparado, reconstruído em resolução plena em torno de uma escada de tamanhos de janela (o anexo do cemitério detalha isso) — quatro falhas estruturais permaneceram, e elas moldaram o método sucessor :

Primeiro, a escada de janelas degenera onde quer que uma janela veja principalmente dados no nível do clip (janelas finas perto da borda, janelas grossas bem no interior do buraco), e um ajuste degenerado (covariância perto de zero, inclinação perto de zero) preenche de forma plana, aproximadamente no nível do clip. Pior, a transição entre escalas consecutivas segue um contorno de profundidade constante dentro do buraco, e a discordância das duas escalas imprime um arco visível ao longo dele.

Segundo, o amortecimento $\epsilon$ acima compete com a variância local do guia, de modo que ele silenciosamente esmaga a inclinação onde quer que essa variância seja pequena — o que protege conteúdo ruidoso por acidente e achata gradientes suaves e limpos (como o céu) pelo mesmo mecanismo. Uma constante, duas consequências opostas, nenhum valor que sirva a ambas.

Terceiro, o sensor não sofre clipping abruptamente : ele comprime os últimos poucos por cento abaixo da saturação, de modo que a faixa de pixels que a reconstrução trata como âncoras confiáveis é registrada sistematicamente baixa demais.11 Mesmo um oráculo que reconstruísse o interior recortado exatamente ficaria então visivelmente mais brilhante do que o anel enviesado ao qual deve se juntar : a emenda está nos dados, não na estimativa, e apenas corrigir os dados (a inversão do joelho) pode removê-la.

Fourth, the whole method rests on one hypothesis : neighbouring channels rise and fall together, so a clipped channel is a measurable affine function of a surviving one. And some content (a sky whose hue itself drifts, iridescence, colour texture finer than the window) simply does not satisfy it. There, the guide contains no information about the missing channel for any estimator, so the design must measure that ($R^2$) and change the information source altogether : spatial smoothness instead of inter-channel transfer.

Fixing those took more than a fix — it took a change of paradigm.

The new method : harmonic transposition

This section documents the method that ships today — its premises, its optimization problem, its algorithm, and how the production implementation drifts from the research prototype. Measured results, performance, and the general findings each have their own section below. Harmonic transposition ships as its own reconstruction mode next to the original guided laplacians : the 2021 à-trous method keeps its historical name and design — unchanged except for the shared border-gather fix, which improves its own scores — and the new method is an explicit opt-in. The reconstruction core is agnostic to the sensor mosaic and runs on Bayer and X-Trans alike, on the processor and inside an OpenCL pipe, where each clipped region is solved on whichever side of the bus measured faster (see the performance section).

Intuição e contexto

The repair above changes what the guided filter transfers ; harmonic transposition changes how the model travels. Wherever at least one channel survived, reconstruction is a regression problem, not an inpainting problem : the valid channels are measured data, present at every pixel of the blown zone, and the only unknown is the local relationship (the colour-line) between them and the clipped channel. The 2021 method — and its corrected, full-signal rebuild — evaluated that relationship wherever a statistics window could reach, and stitched the evaluations together ; every stitch was a seam, and windows deep inside a large hole reached nothing at all. Harmonic transposition inverts the transport : fit the colour-line once, where the data supports it, then diffuse the coefficients of the model — not the pixel values — across the blown zone as smooth fields, and only then evaluate them against the measured surviving channels at every pixel. Coefficients are smooth by nature where values are not ; the guides re-inject the full-resolution structure at evaluation time. Why this is a difference in kind rather than degree — and what it generalizes to — is spelled out in the theoretical findings.

Everything else the 2021 method got right is kept, on a sounder footing : the colour-line model and its windowed weighted least squares (the guided filter’s regression), the biharmonic dome for guide-less magnitude, chrominance carried as bounded ratios, the preference for smooth continuations, and the grain regeneration. What is new besides the transport : per-region segmentation at full resolution (cost scales with the clipped area, not the image), a measured sensor-rolloff inversion that debiases the near-clip band before anything is fitted (failure 3 above), fits gated and trusted by their own measured quality $R^2$ (failure 4), and one design rule applied everywhere, derived from the seam-energy law of the theoretical findings : no step may hand off between estimators that disagree (failure 1).

O problema de otimização

Neither step assembles a global energy and calls a named solver, but each is the local update rule of a variational problem, and together they make the objective explicit. On the clipped region $\Omega$ the reconstruction minimizes four coupled energies on the model.

1. Cross-channel affine consistency. Where a valid guide $g$ exists, each clipped channel should be an affine function of the guide : the guided filter is the exact minimizer of

$$ E_{\text{affine}} = \sum_{\omega}\sum_{c}\sum_{i \in \omega} w_i \Big[ \big( a_{c} \, u_g(i) + b_{c} - u_c(i) \big)^2 + \varepsilon\, a_{c}^2 \Big], \qquad w_i = [\,i \text{ valid}\,]. $$

Fitting on the full values $u$ (not the detail) is what makes the minimizer carry the local average, so $E_{\text{affine}}$ recovers magnitude and texture together.310 This term is trusted in proportion to the fit’s squared correlation $(R^2)^2$; where $R^2 \to 0$ the channel is instead held to its own second-order smoothness (a per-channel biharmonic term $\int_\Omega (\Delta u_c)^2, \mathrm{d}\Omega$), so the objective never rewards a colour-line the data do not support. The squaring sharpens that trade so a middling correlation leans on the smooth fallback rather than a discontinuous cross-channel guess.

1b. Anisotropic coefficient transport (the shipped form of term 1). The affine fit of term 1 defines its coefficients only where a window holds enough trusted data ; across the rest of the blown zone the model itself is the unknown. The shipped method extends it by minimizing an anisotropic Dirichlet energy on the coefficient planes,

$$ E_{\text{transport}} \;=\; \sum_{p \,\in\, \{a,\, b,\, d,\, R^2\}} \int_{\Omega} \nabla p^{\top} D \,\nabla p \;\mathrm{d}\Omega, \qquad p\big|_{\text{anchors}} = p_{\text{fit}}, $$

where the anchors are the gated fits of term 1 (enough trusted mass, $R^2 > 0.25$, bounded slopes) and $D$ is the variance-adaptive steering tensor of step 3, built from the measured guide structure : gradient-dominant on a clean halo ramp (the model travels radially inward from the rim), isophote-dominant where a hard edge crosses the zone (colour-lines must not mix across an object boundary). Its Euler–Lagrange equation $\operatorname{div}(D \, \nabla p) = 0$ is the steered fill ; with $D = I$ it reduces to the plain harmonic fill. The evaluation $\hat u_c = a\,u_{g_1} + b\,u_{g_2} + d$ against the measured guides closes the term : the energy transports the model, and the data restores the detail.

2. Magnitude curvature (fallback). Where no guide survives, fine detail is lost but the low-frequency shape is not: it should continue the surrounding curvature rather than flatten. For a partially-clipped channel this is the per-channel biharmonic term $\int_\Omega (\Delta u_c)^2, \mathrm{d}\Omega$ already invoked above. For the all-clipped core it is applied once to the summed luminance $L_\text{sum} = R+G+B$, one shared dome instead of three divergent ones:

$$ E_{\text{bihar}} = \int_\Omega (\Delta L_\text{sum})^2\,\mathrm{d}\Omega, \qquad \Delta^2 L_\text{sum} = 0, \qquad L_\text{sum}\big|_{\partial \Omega} = L_{\text{sum}}^{\text{valid}} , $$

anchored to the core’s true valid rim; its Euler–Lagrange equation $\Delta^2 L = 0$ is the domed fill above.

3. Chrominance smoothness (fallback). Where no guide survives, the reconstructed ratios $r = \text{RGB}/L_\text{sum}$ should be smooth and match the rim, optionally biased toward flatness:

$$ E_{\text{chrominance}} = \int_\Omega \Big( \lVert \nabla r \rVert^2 + \lambda \, \lVert r \rVert^2 \Big)\, \mathrm{d}\Omega, \qquad r\big|_{\partial \Omega} = r_{\text{valid}} . $$

Its Euler–Lagrange equation is the screened-Poisson equation the diffusion integrates,4 and the recombination $\text{RGB} = L\cdot r$ closes the joint fill.

The energies act on complementary pixels : the affine term where a channel survives (weighted by its correlation $R^2$), the per-channel biharmonic term where it does not, and the shared luminance dome plus chrominance diffusion only in the all-clipped cores — the latter minimized subject to an inequality : the diffused ratio of a clipped channel can never drop below its saturation floor in ratio space, $r_c \geq c_{0,c}/L_\text{sum}$, which turns the chrominance term into an obstacle problem (the math is in the chrominance subsection). The reconstructable set itself is per channel : $\Omega_c$ holds the pixels above the clip threshold — extended down to $0.9$ of it for channels whose sensor rolloff engaged (the band override of step 2), where the floor $c_{0,c}$ is the knee-corrected measurement rather than the saturated reading. Each energy is minimized by its own stage : the affine term by the guided fits (one window sized to the region’s reconstruction radius) ; the transport term by the anchored, steered relaxation of step 3 ; the smoothness terms by their direct linear solves. There is deliberately no posterior smoothing energy acting on the output : an earlier design ironed seams after the fact with an uncertainty-weighted regularizer, and retiring it is the point — the seams are never created (see the graveyard). This is the same engineering philosophy as diffuse or sharpen: a stack of local, physically-motivated update rules whose combined fixed point is the reconstruction, rather than one monolithic inverse problem.

O algoritmo

Everything happens on the raw mosaic, in linear scene-referred RGB, before demosaicing, and at full resolution. The method rebuilds each clipped channel from the channels that survived (along the local colour-line, trusting it in proportion to how well it actually holds), and where no channel survived it rebuilds one joint luminance dome and carries the surrounding chrominance inward.

The mosaic preparation — bilinear demosaic to a throwaway RGB, local channel normalization, and the per-channel clip mask — is shared with the 2021 method and described in its algorithm section, with one addition and three refinements. The addition : for the all-clipped cores a scalar magnitude $L_\text{sum}$ (the summed luminance $R+G+B$) is split from the chrominance $\text{RGB}/L_\text{sum}$ — each channel divided by the summed luminance, a bounded, brightness-free description of the colour that this article uses consistently under that name — so the two can be reconstructed by different means : a dome for the magnitude, diffusion for the chrominance. The refinements, each traced from a measured rim artifact :

  • masks are binary, end to end. The 2021 mode feathers its mask ; the successor keeps every validity and compositing mask hard (its one smooth weight is the joint core’s blurred hand-over, step 7 of the algorithm, which blends two reconstructions — it never reclassifies measurements). The per-channel validity masks gate every fit and evaluation, and feathering them reclassified rim-clipped photosites — whose raw values sit at the detection threshold, biased low under sensor rolloff — as valid anchors on oblique contours, dragging the rim reconstruction toward the clip level (a $\sim 10$ px sagging ramp against ground truth). The compositing weight is a hard switch : valid photosites keep their measurement exactly, clipped photosites take the pure reconstruction (a feathered alpha was measured to change nothing once the two fixes on this list are in — the retirement is documented in the graveyard).
  • clipped raw values are floors, never blend targets. On clipped photosites the raw reading is a lower bound, not a measurement : the composite writes $\max(\text{raw}, \text{reconstruction})$, never a blend toward the biased reading (the old feathered blend printed a V-shaped dip through the raw value at every contour).
  • borders mirror. The bilinear gather’s border ring used to copy the centre photosite into all three channels and key all three clip flags on the centre’s own channel — corrupted guides and dashed masks along the first and last rows and columns, which anchored the border-row fits at the clip level. Reflected neighbour indexing (the Bayer pattern is 2-periodic, so mirroring preserves each neighbour’s colour) restores the interior logic on the borders ; this fix is shared with the 2021 mode.

Every ground-truth scene improved on both metrics from these three refinements alone — the rolloff sky’s error dropped by a third — because the bench’s rim rings are exactly where biased anchors used to leak in.

The reconstruction’s conceptual goal can be stated in one sentence : wherever at least one channel survived, reconstruction is a regression problem, not an inpainting problem : the valid channels are measured data, present at every pixel of the clipped zone, carrying the true structure of the scene. The algorithm’s one design rule, learned the hard way, is that no step may hand off between estimators that disagree (the seam-energy law of the theoretical findings). Every stage below is therefore either seam-free by construction, or debiases the data so that the estimators agree.

The step-by-step procedure, as implemented in process_harmonic_bayer (and its X-Trans twin) :

The numbers in the graph refer to the steps detailed below it.

flowchart TD
  subgraph P ["once per image"]
    direction TB
    A["raw mosaic"] --> B["1 · interpolate
+ clip masks"] B --> K["2 · knee inversion"] K --> S["1 · depth map
+ segmentation"] end subgraph R ["for each clipped region"] direction TB CF["3 · colour-line fits
(magnitude + texture)"] --> HF["4 · fine-detail refit"] HF --> SD["5–6 · floors
+ self-dome"] SD --> JC["7 · all-clipped core"] JC --> AN["8 · chrominance coherence"] end S --> CF AN --> O["remosaic + composite"]

1. Detection, interpolation, segmentation. The raw colour-filter-array mosaic is bilinearly interpolated, with per-channel clip flags raised at $0.995\,c$ for clip level $c$ and a binary per-channel validity mask ; the Euclidean distance transform then gives each pixel its depth $\delta$ inside the clipped zone, and connected-component segmentation groups the clipped pixels into regions, each carrying its distance-transform reconstruction radius.

2. Sensor rolloff (knee) inversion. Real sensors compress the last few percent below saturation, so the near-clip band $[0.8\,c, 0.995\,c)$ holds values biased low. (The band is cut generously : on sensors that clip hard rather than roll off, the no-op guarantee below makes the extra width free.) The reconstruction extrapolates unbiased estimates over this band, so the two disagree at the detection contour : a seam no weighting can remove. The fix debiases the data itself : a windowed joint regression predicts each band value from the fully-trusted channels, the (measured, predicted) pairs are pooled into 24 bins over the band, and the per-bin median lift is accepted only when statistically significant — median above twice its standard error, a $\approx 95\,\%$ confidence gate, with at least 100 votes so the error estimate itself is stable ; 24 bins is as fine as the band can be cut while keeping every bin that populated :

$$ \hat{k}^{-1}(v) = v + \operatorname{median}\big\{\, \hat v_i - v_i \;\big|\; v_i \in \text{bin}(v) \,\big\} $$

where $k$ is the sensor’s unknown compression, so $\hat{k}^{-1}$ is the estimated correction — the function that takes a measured value $v$ from the band and returns what the sensor should have recorded. The $v_i$ are all the measured band pixels whose value falls in the same bin as $v$ ; each $\hat v_i$ is what the colour-line regression predicts that pixel should be, judging from its fully-trusted neighbouring channels. Their difference $\hat v_i - v_i$ is one pixel’s vote on how much the sensor under-recorded at that level, and the bin’s correction is the median of its votes — robust to the outliers a mean would follow. The resulting curve is then made monotone and raise-only. On hard-clipped (unbiased) data every bin median is zero within noise, so the correction has a no-op guarantee, verified bit-exact on the four hard-clip synthetic cases, while on pk1synth (the one synthetic scene generated with an analytic sensor rolloff, so its ground truth traces the real knee) the blind estimate matches the true curve to a root-mean-square error of $7\cdot10^{-4}$ on the green channel and $1.8\cdot10^{-3}$ on the sparser red one :

The blind knee estimate against the ground truth, on the one synthetic scene with an analytic sensor rolloff (pk1synth). Circles : the true corrected value, read from the (ground truth, clipped) pixel pairs, per measured-value bin. Lines : what the estimator infers from the clipped mosaic alone, never having seen the truth. The dotted diagonal is the identity — a sensor with no rolloff. Reproduce with make_knee_figure.py.
Two implementation details matter : estimation runs on a $2\times 2$ quad-binned copy of the raw mosaic (each 2×2 Bayer cell yields one co-located red, green (averaged) and blue sample), never on the bilinear interpolation : the interpolation samples each channel through a different spatial filter depending on its position in the mosaic pattern, and that alternating error is the same size as the knee signal it would drown ; and the correction is applied both to the reconstruction anchors and to the composited output, while clip detection stays keyed on the measured values.

The band override. The lift restores the band’s level, but a value-map cannot restore a slope the sensor never recorded : where the compression flattened the band to a near-constant, the lifted band is a near-constant too, and its junction with the rising reconstruction prints a fine double contour under edge magnification (the band is measurably $7$–$14\times$ flatter than ground truth, bracketed by two gradient spikes). The information is not lost, though — the guides are unclipped through the band and carry the true slope. So for every channel whose rolloff engaged, the detection extends below the threshold into the deep band : those pixels are reconstructed by the colour-line model like any clipped pixel, with their knee-lifted measurement as their per-pixel saturation floor, and the fits anchor on knee-corrected data below the band. The floor makes the override conservative by construction — on noisy real images the model rarely exceeds the lifted measurement, and the output stays pixel-identical ; on the rolloff bench scene, where the model genuinely knows the slope, the zone error drops another factor of five when the override lands (RMSE $0.012 \to 0.0025$ measured then ; $0.0053$ in the shipped build, after the later compositing rounds took their own bites) and the contour gradient profile becomes indistinguishable from ground truth. Channels without a measured rolloff keep the plain detection : on hard-clipping sensors the band is trustworthy data and stays anchored.

3. The coefficient field. For each clipped channel $v$ with guides $u_1, u_2$, a windowed weighted least-squares fit is computed at a single scale $\sigma = \operatorname{clip}(r/6, 8, 64)$ (with $r$ the region’s reconstruction radius — the window’s $\pm 3\sigma$ support then spans the radius, so even the deepest pixel’s fit reaches valid data ; the floor keeps enough samples for a stable fit on tiny regions, and the cap bounds the cost of huge ones), over the pixels where all three channels are valid :

$$ \begin{aligned} (a, b, d)(x) &= \arg\min_{a,b,d} \sum_y w(y)\, G_\sigma(x - y)\, \big( v(y) - a\, u_1(y) - b\, u_2(y) - d \big)^2 \\ \hat v(x) &= a(x)\, u_1(x) + b(x)\, u_2(x) + d(x) \end{aligned} $$

where $x$ is the pixel whose local model is being built and $y$ runs over its neighbours. The window $G_\sigma(x-y)$ is a Gaussian that weighs neighbours by distance (full weight nearby, fading over roughly $\sigma$ pixels), and $w(y)$ is the trust mask — one on pixels where all three channels hold real measurements, zero elsewhere, so clipped pixels never vote. The first line asks, at every $x$ : which slopes $a, b$ on the two guides and which offset $d$ best explain the clipped channel on the trusted neighbourhood ? The second line then uses that local model : the estimate $\hat v(x)$ reads the two guides at $x$ itself and maps them through the fitted relation. In practice the minimization is never run per pixel — it is solved from ten blurred moment planes (a trusted-mass count, three means, six second moments, gathered in three four-channel blurs) through the $2\times2$ normal equations. The moments are accumulated about each channel’s per-region mean : in float arithmetic the raw $E[u^2] - E[u]^2$ form cancels catastrophically on smooth content (the squared mean dwarfs the variance) and the fit’s division amplifies the surviving digits into device-dependent slope noise — centering removes the cancellation, and the slopes and $R^2$ are invariant under the shift while the intercept is unshifted right after the fit. The system is solved with a relative Tikhonov (ridge) damping $\lambda = 10^{-3}\,(\operatorname{var} u_1 + \operatorname{var} u_2)/2$ that scales with the signal instead of eating weak-but-real slopes. The $10^{-3}$ is the smallest factor that stabilized degenerate windows on the bench — anything larger starts flattening real slopes.

The novelty is what happens next : instead of evaluating each pixel with whatever fit its own window could reach (the ladder’s way), the coefficient planes themselves are diffused across the clipped zone, steered by the measured guide structure, and only then evaluated against the measured guides. Coefficients are smooth by nature where values are not : diffusing them transports the model into the zone, and the evaluation restores the full local structure carried by the valid channels. No scales, no depth gates, no level-set writes (no rings of equal depth stamped separately) — no seams, by construction. This transport of the model rather than the content is the article’s most transportable idea, and how it differs in kind from the guided filter’s own coefficient blur is stated in Three results we believe are general. A pixel qualifies as a diffusion anchor only if its window held enough trusted mass and its fit is sane : the fit’s $R^2$, the coefficient of determination, is the share of the channel’s local variance that the colour-line explains (1 means the guides predict the channel perfectly, 0 means not at all), and anchors require $R^2 > 0.25$ with bounded slopes. Both gates are calibrated from measurements, not taste : content with no colour-line at all still scores $R^2 \approx 0.25$–$0.6$ (against $\approx 0.9$ where the colour-line is real), so the gate only rejects fits worse than pure decorrelation — numerical accidents, not weak models ; and physical colour-line slopes are bounded by the channels’ saturation ratios (under $4$ on every camera measured), so the $|a| < 64$ bound sits an order of magnitude above anything physical, and only degenerate near-zero-variance windows — whose exploding slopes would poison the diffusion boundary — ever trip it. The diffusion is a coarse-to-fine Jacobi fill : each unknown is repeatedly replaced by the average its neighbours imply. Three terms carry the whole scheme :

  • Cell. The fill does not run per pixel. The coefficient planes live on a coarse grid of pitch $\sigma/4$ (clamped to at most $8$ px), and a cell is one node of that grid — one $\sigma/4$-sized block of pixels. The coefficients come out of $\sigma$-wide fit windows, so four cells per $\sigma$ oversample them, and the pitch clamp bounds the final bilinear upsampling error.
  • Anchor. A cell whose fit window passed the trust gates above — it holds a coefficient actually measured on valid data.
  • Pinned. The sweeps treat anchors as fixed boundary data and only ever rewrite the unknown cells between them.

This shape is chosen for robustness. A float conjugate-gradient on the near-singular pure-harmonic system diverges stochastically when the zone reaches the region border. Jacobi with pinned anchors cannot : each update is an average of neighbours, so the relaxation obeys the maximum principle — no unknown can ever leave the range of the anchor values.

Convergence comes from the pyramid’s depth, not from the sweep count. The coarsest level starts from a flat anchor mean — the farthest possible state from the solution — and Jacobi’s slowest error mode on a hole $N$ cells wide decays in $\mathcal{O}(N^2)$ sweeps. So the pyramid keeps halving until the coarsest grid’s long side is at most $8$ cells ($8^2 = 64$ sweeps to relax, comfortably inside the budget), and every level then runs the same flat $100$ sweeps — the finer levels only correct local interpolation error. A shallower pyramid with a fixed count measurably truncated the fill on deep holes — the numbers are in the general results below.

The transport itself is not uniform either : it is steered by the measured guide structure. A steering plane $L_\text{mean}$ — the mean of the valid channels wherever at least one survives, the flat plateau mean in the all-clip core (where the tensor then degenerates to the identity) ; this is a different luminance from the summed magnitude $L_\text{sum}$ that the dome and the ratios use — is downsampled to each pyramid level, and the neighbour averages are weighted by the anisotropic tensor

$$ D \;=\; \big[\, m + (1-m)\,c_2 \,\big]\; t\,t^{\top} \;+\; \big[\, m\,c_2 + (1-m) \,\big]\; g\,g^{\top}, \qquad c_2 = e^{-\lVert\nabla L_\text{mean}\rVert / (4\,\langle\lVert\nabla L_\text{mean}\rVert\rangle)}, $$

where $g$ is the unit gradient direction of $L_\text{mean}$ (uphill), $t$ the unit isophote direction, and $\langle\lVert\nabla L_\text{mean}\rVert\rangle$ the gradient magnitude averaged over the region — the normalization that makes the damping exposure-independent — (along the level lines), and $c_2$ the same edge-crossing damping as the step-8 chrominance pass. Normalizing the gradient by its own regional mean makes that damping scale-free — only gradients well above the halo’s average read as boundaries, the $4$ setting how soft the reading is. The blend weight $m \in [0, 1]$ is the edge probability, measured from the trend-corrected windowed variance of $L_\text{mean}$ :

$$ m = \frac{v}{v + (k\,\bar L_\text{mean})^2}, \qquad v = \max\!\big(\operatorname{var}_w(L_\text{mean}) - \tfrac{4}{3}\lVert\nabla L_\text{mean}\rVert^2,\; 0\big), \qquad k = 0.15, $$

where $\operatorname{var}_w$ and $\bar L_\text{mean}$ are the windowed variance and mean of the steering plane, and the $\tfrac{4}{3}\lVert\nabla L_\text{mean}\rVert^2$ term subtracts the variance the local ramp explains : a pure ramp of slope $\nabla L_\text{mean}$ seen through a window of spatial variance $\sigma_w^2$ has intensity variance $\sigma_w^2 \lVert\nabla L_\text{mean}\rVert^2$, and the window here (two $3\times3$ box passes) has $\sigma_w^2 = 2 \cdot \tfrac{2}{3} = \tfrac{4}{3}$ per axis. A smooth halo gradient, however steep, therefore leaves no residual, while a hard edge leaves variance no ramp can account for. The threshold $k$ — the relative contrast above which residual texture is read as a boundary — is the one constant of the transport that could not be derived, so it was calibrated by an empirical fine sweep on the six ground-truth scenes : every scene stays at or below the isotropic error over the whole range $k \in [0.14, 0.25]$ (the design is not fragile to it), and $k = 0.15$ is the value that improves every scene simultaneously and takes the occlusion win with margin. The two limits of $m$ read naturally. On a clean halo ramp ($m \to 0$), $D \to g\,g^{\top} + c_2\,t\,t^{\top}$ : the model travels radially, along the brightness ramp from the rim inward, where its information actually lives. Where a hard edge crosses the blown zone ($m \to 1$), $D \to t\,t^{\top} + c_2\,g\,g^{\top}$ with $c_2$ small : the transport runs along the boundary and refuses to cross it — an edge inside the zone means the content beyond follows another colour-line, and mixing the two models across it is exactly the occlusion failure the graveyard documents. The discretization uses the nonnegativity-preserving stencil shared with the step-8 diffusion (all eight neighbour weights $\geq 0$), so the steered fill remains a convex combination of its anchors : the maximum principle survives the steering. A $10^{-4}$ weight floor keeps every cell connected where the tensor collapses to near-zero ; four orders of magnitude under the working weights, it never competes with the steering. Pixels with a single surviving guide get the same treatment with a one-guide fit.

One ordering subtlety completes the step. The deepest channel — the one with the most clipped photosites, whose zone contains the cores where several channels are blown at once — is not evaluated right away : its diffused coefficients are stashed, and the evaluation runs last, after the other clipped channels have been reconstructed, so every guide it reads is a continuous surface. Evaluating it against a guide that jumps from measured values to a clip plateau would print that guide’s own clip contour into the result as a visible arc. Deep inside the core, where those guides are themselves reconstructions (so each hop compounds error), the estimate blends back toward the direct one-guide fit through a smooth weight — smooth weight times smooth fields leaves no level set to print.

The fit quality $R^2$ is diffused alongside $(a, b, d)$ as a fourth plane, on the broader mass-only anchor set (it stays bounded even where the fit does not).

4. Hybrid Laplacian-band guiding of the high frequencies. The full-signal fit transfers the guides’ fine texture with the gains implied by the total covariance ; where the colour-line is weak that texture does not belong on the reconstructed channel. The estimate is split at $\sigma/4$ and its detail band is rebuilt from two candidate sources : the damped transfer (the full-signal high frequencies scaled by the diffused $R^2$) and a dedicated detail-band colour-line (the 2021 method’s namesake, returned), fit on the high-frequency planes with $R^2$-shrunk gains (on a zero-mean band, shrinkage is the correct estimator : there is no magnitude to lose, only noise to not print). The two are blended by quadratic min-energy odds,

$$ w = \frac{e_d^2}{e_d^2 + e_g^2}, \qquad e_{d,g} = \text{blurred } |\text{HF}_{d,g}|, $$

which needs no content discriminator : a mixed-window gain misfiring at an object edge shows up as a local spike of high-frequency energy, so the failure detects itself and the damped path takes over exactly there. This is what removes the last residual of the rolloff scene (its excess-edge score falls from 1.48 to 0.94 against a ground-truth floor of 0.69 : the decompressed band noise no longer prints — the rim-bias fixes of the final build bring it to 0.74, and the band override of step 2 to 0.64, the results table) while improving the textured cases the pure detail-band fit used to break.

5. Soft saturation floor. The physical floor (a clipped channel is at least its saturated reading $c_0$) is applied as a rounded constraint, $\tfrac{1}{2}\big(e + c_0 + \sqrt{(e - c_0)^2 + (0.02\,c_0)^2}\,\big)$ : the hard $\max(e, c_0)$ prints the binding contour as an edge wherever a weak prediction oscillates around saturation. The $0.02\,c_0$ transition width is two percent of the saturated value — below anything visible, but wide enough that the constraint’s gradient never jumps.

6. Depth-gated, hue-coupled self-dome. Where the model is doubtful and the pixel is shallow, a smooth self-continuation takes over, built hue-coupled (the channels are coupled through one shared chrominance, so the perceived hue cannot drift) : one shared biharmonic luminance dome times a harmonically-filled chrominance, never three independent channels, so the fallback cannot split the hue toward green/magenta (the failure that kept its per-channel ancestor disabled). The hand-over weight is :

$$ \text{dome fraction} = \big(1 - S_{0.4}^{0.85}(R^2)\big)\; e^{-\left(\delta / 1.5\sigma\right)^2} $$

where $\delta$ is the pixel’s depth inside the clipped zone (its distance to the nearest valid pixel), $\sigma$ the fitting scale, and $S$ a smoothstep, a soft ramp from 0 to 1 between the two thresholds, here mapping fit quality below $0.4$ to “doubtful” and above $0.85$ to “trusted”. The band edges and the $1.5\sigma$ depth scale were tuned on the ground-truth bench ; the depth scale ties the dome’s reach to the fitting window’s own reach, so the hand-over happens where the fit genuinely runs out of samples. The two factors answer two different questions : $R^2$ asks is the colour-line real here, and depth asks is the dome trustworthy here (biharmonic extrapolation is excellent near the rim and degrades with distance). Depth is the only signal we found whose distributions actually separate correlated deep zones from decorrelated shallow content (see the graveyard) ; deep interiors always stay on the coefficient field.

7. Joint core, feathered. All-clipped pixels have no guides : the shared biharmonic luminance dome and the screened rim-chrominance diffusion of the rebuild reconstruct them, unchanged — but the composite is now feathered over a blurred all-clip mask instead of written through a hard one. The dome and the diffused chrominance are both valid past the hole boundary, so blending them into the clipped channels of the surrounding ring is continuous in space at no cost to the core itself.

8. Chrominance coherence. The structure-steered chrominance of the all-clip core is now solved directly. The unknown $u$ is each chrominance plane in turn ; the equation is $\mathrm{div}(D\,\nabla u) = 0$, the steady state of the anisotropic diffusion of the theory section and, equivalently, the exact minimizer of its weighted smoothness energy $\int_\Omega \nabla u^\top D\,\nabla u\,\mathrm{d}\Omega$, with the coefficient-field results as Dirichlet anchors, fixed boundary values the solve must honour. The tensor is built from the recovered luminance (the reconstruction’s own output, smoothed by two box-blur passes) : with $g$ the unit gradient of that smoothed luminance and $t = g^\perp$ the isophote direction,

$$ D = t\,t^\top + e^{\,-\lVert \nabla L_\text{sum} \rVert / (4\,\langle \lVert \nabla L_\text{sum} \rVert \rangle)}\; g\,g^\top $$

that is : conductance one along isophotes, exponentially damped across them, the damping normalized by the image’s own mean gradient magnitude $\langle \lVert \nabla L_\text{sum} \rVert \rangle$ so the steering does not depend on exposure (this tensor reads the recovered summed luminance — the coefficient fill’s twin form reads $L_\text{mean}$). The discretization is Weickert’s nonnegativity-preserving stencil, a symmetric positive-definite M-matrix, so the discrete maximum principle holds by construction : the diffused ratios can never overshoot the rim chrominance, which the earlier explicit flow could. The sparse Cholesky factors the core once and back-substitutes the three channels. (A solver bake-off sits behind this choice : see the graveyard for the instructive failure of the other exact formulation.) The final remosaic composes the output from the knee-corrected CFA, so the debiased band reaches the file, not only the fits.

As regras de atualização

The optimization problem states what the reconstruction minimizes and the algorithm narrates its stages ; the discussion below analyses the design — what the parameters represent, how they were tuned, how the choices relate to established theory. What an implementer starting from scratch needs are the actual signal updates : which equation writes which plane, in which order. Here is the complete catalogue, in execution order, for one region. Planes : per-channel estimates $u_c$ (initialized to the bilinear interpolation of the measured mosaic, knee-corrected), binary validities $v_c$, per-pixel floors $c_{0,c}$ (the knee-corrected measurement of every reconstructable pixel), summed luminance $L_\text{sum} = \sum_c u_c$ and ratios $r_c = u_c / L_\text{sum}$ (the steering plane $L_\text{mean}$ of rule 1 is the valid-channel mean, a separate quantity).

1. Fit and transport the colour-line (steps 2–3 of the algorithm). The fit is on the full pixel values — never the mean-subtracted detail — and uses only the samples where the involved channels are valid. In the one-guide form, over the Gaussian window $\omega$ :

$$ a_c = \frac{\operatorname{cov}_\omega(u_g, u_c)}{\operatorname{var}_\omega(u_g) + \varepsilon}, \qquad b_c = \bar u_{c,\omega} - a_c\, \bar u_{g,\omega}, \qquad R^2 = \frac{\operatorname{cov}_\omega(u_g, u_c)^2}{\operatorname{var}_\omega(u_g)\,\operatorname{var}_\omega(u_c)}, $$

with $\varepsilon$ a relative Tikhonov damping guarding the division where the guide is flat ; the two-guide form solves the analogous $2\times2$ normal equations for $(a, b, d)$ from the ten Gaussian-blurred moment planes. The slope is not sign-clamped (an early ladder revision clamped it ; the shipped fits are guarded instead by the anchor gates $R^2 > 0.25$ and $|a| < 64$). Two more gates an implementer must not skip : the fit windows are weighted by a soft luminance affinity $\min(L_\text{sum} / 0.35\,\bar L_\text{sum}^{\,\text{rim}}, 1)^2$ — samples much darker than the rim (occluders) barely vote — and a window only anchors the transport where its weighted mass exceeds both an absolute floor (0.05) and a quarter of its unweighted valid mass, so occluded windows defer to the fill rather than fit the occluder. Because the fit is on values, the intercept carries the local average of the colour : the prediction inherits the level of the surrounding valid data, not just its texture, so where the guide keeps rising into the highlight the estimate follows it — above the clip value. In practice the fit is never run per pixel : the moments are Gaussian-blurred product planes, centered on the per-region valid means against float cancellation.

Gate the anchors ($R^2 > 0.25$, bounded slopes, trusted mass), then diffuse each coefficient plane by anchored Jacobi sweeps on the coarse grid — the update of one sweep at a non-anchor cell $i$, with the eight Weickert weights $w_{ik}$ of the tensor $D$ :

$$ a_i \;\leftarrow\; \frac{\sum_{k \in \mathcal{N}_8(i)} w_{ik}\, a_k}{\sum_{k \in \mathcal{N}_8(i)} w_{ik}}, $$

anchors held fixed, 100 sweeps per pyramid level, coarsest level seeded with the anchor mean and each finer level seeded by bilinear upsampling of the coarser solution ; same update for $b$, $d$ and $R^2$. Then evaluate the transported model against the measured guides, at every pixel where the target is clipped and both guides are valid :

$$ u_c(x) \;\leftarrow\; a(x)\, u_{g_1}(x) + b(x)\, u_{g_2}(x) + d(x). $$

Pixels with a single valid guide take the same update from the one-guide fit $u_c \leftarrow a\, u_g + d$. The deepest channel’s evaluation is deferred until the other clipped channels are rebuilt, so its guides are continuous surfaces.

2. Rebuild the high frequencies (step 4). Split $u_{g}$ into low frequency $\bar u_g$ (Gaussian at $\sigma/4$, floored at 2 px — the moments use the fit’s $\sigma$, the band split does not) and detail $u_g - \bar u_g$ ; form the two candidates — the guide transfer $h_g = a\,(u_{g_1} - \bar u_{g_1}) + b\,(u_{g_2} - \bar u_{g_2})$ and the damped self-detail $h_d = R^2 (u_c - \bar u_c)$ — and blend them by quadratic min-energy odds on their local (Gaussian-blurred) energies $e_g, e_d$, exactly as step 4 states (a hard switch would reintroduce a hand-off seam) :

$$ u_c \;\leftarrow\; \bar u_c + w\, h_g + (1 - w)\, h_d, \qquad w = \frac{e_d^2}{e_d^2 + e_g^2}. $$

3. Soft saturation floor (step 5), everywhere a channel is reconstructable, with $w = 0.02\, c_{0,c}$ :

$$ u_c \;\leftarrow\; c_{0,c} + \tfrac{1}{2}\left( (u_c - c_{0,c}) + \sqrt{(u_c - c_{0,c})^2 + w^2}\right). $$

4. Depth-gated self-dome (step 6). Solve one shared biharmonic continuation of the luminance over the hole, $\Delta^2 L_b = 0$ with $L_b$ anchored on the rim (direct sparse Cholesky on the 13-point discrete bilaplacian, coarse grid — the same solver the implementation section documents), split its chrominance from the rim ($\bar r_c$, harmonic fill), and blend by the dome fraction $f$ :

$$ u_c \;\leftarrow\; (1 - f)\, u_c + f\, L_b\, \bar r_c, \qquad f = \big(1 - S_{0.4}^{0.85}(R^2)\big)\, e^{-(\delta / 1.5\sigma)^2}, $$

with $\delta$ the pixel’s depth inside the clipped zone (its distance to the nearest valid pixel — the only meaning $\delta$ carries in this article).

5. All-clipped core (steps 7–8), on pixels where no channel survived. Magnitude : the biharmonic dome $\Delta^2 L = 0$ anchored on the (already reconstructed) annulus. Chrominance : first the screened-Poisson rim fill per channel, $(\Delta - \lambda)\, r_c = -\lambda\, \bar c_c$ with $\bar c_c$ the mean valid chromaticity of the padded region and $\lambda$ the flat-colour reaction (direct sparse solve, shared factorization) ; then the structure-steered pass. Its primary form is the divergence-form exact solve of step 8 (edge-weighted 8-neighbour graph Laplacian, direct sparse factorization) ; cores above $2^{14}$ unknowns take the explicit trace-form pyramid instead — steps under the obstacle, iterated 240 times per level :

$$ r_c \;\leftarrow\; \max\!\Big( r_c + 0.18 \,\big( D_{xx} \partial_{xx} r_c + 2 D_{xy} \partial_{xy} r_c + D_{yy} \partial_{yy} r_c \big),\; \frac{c_{0,c}}{L_\text{sum}} \Big), $$

followed by a 60-sweep projected polish at full resolution (activity-gated : it runs only where some pixel actually sits on its floor). Reassembly, per pixel of the all-clip core — the only pixels this pass writes ; partial-clip pixels were settled by rules 1–4 and act as its anchors :

$$ u_c \;\leftarrow\; L_\text{sum}\, \frac{r_c}{\sum_j r_j}, $$

soft-floored as in rule 3. (The code also carries a magnitude-transfer branch for partially-valid pixels, a ladder-era leftover that the anchor construction makes unreachable — documented here so nobody transcribes it.)

6. Grain and composite. Add the Poissonian grain of the next subsection where requested, then write back through the hard switch : reconstructable photosites take $\max(\text{raw}, u_c)$, valid photosites keep their measurement untouched.

Everything else in the pipeline — the knee estimation, the segmentation, the padding — prepares inputs for these six updates ; nothing else writes a pixel.

Discussion

The operators the algorithm composes are not arbitrary : each is the estimator its energy term prescribes, and each carries measurable properties that the design leans on. This subsection derives them, and closes with the parameter study that sizes the whole method — how far a reconstruction should reach.

Trusting the guide

The colour-line borrow is only as good as the assumption behind it : that the clipped channel and its guide are, locally, affinely related. On a natural surface they are ; on pathological content (independent per-channel gradients, or a boundary between two differently-coloured materials) they are not, and forcing the borrow would paint in structure that does not belong. The fit itself reports how far to trust it : $R^2$ (defined with the fit, in the update rules) is the share of the channel’s local variance the colour-line explains — $1$ where the model holds, $0$ where the borrow is meaningless.

So the method never switches between cross-channel and single-channel filling — it blends them by confidence, falling back to extending the clipped channel’s own gradient inward (per-channel biharmonic inpainting, anchored on that channel’s true valid rim) as the colour-line weakens. The blend weight is the squared confidence $W_e = (R^2)^2$, and the exponent is a measured choice, not taste. Over the clipped pixels, $R^2$ separates the two regimes only with a soft margin : it averages $\approx 0.9$ on natural-like content, $\approx 0.65$ on decorrelated content, and the two distributions overlap by $\sim15\%$ — and no cheaper statistic does better (cross-scale slope stability and inter-guide agreement were both measured to separate worse, so $R^2$ is the honest ceiling). Used linearly, the decorrelated case would still take $\sim65\%$ of a cross-channel estimate that is both wrong and — because the hard guide selection flips there — discontinuous. Squaring pulls the regimes apart : $0.9^2 \approx 0.81$ barely moves the trustworthy case, while $0.65^2 \approx 0.42$ halves the untrustworthy one, so decorrelated pixels lean on the smooth own-gradient fill. Because the margin is soft, though, a faint seam still survives on the most adversarial content : squaring cannot fully hide a discontinuity in the quantity it weights, and that residue is what the never-create-seams design rule exists for (see the graveyard).

Where no channel survives there is no guide and no $R^2$ to weigh, so the reconstruction hands off to the joint core of the next section. The blend is thus gated by guide-validity and weighted by squared correlation, not by a hard count of clipped channels : one mechanism spans the whole range from a one-channel clip (mostly cross-channel) through a two-channel clip (the surviving channel guides) to a fully-blown core (the joint dome). This is what makes the method degrade gracefully as inter-channel correlation falls : on the natural-like test images the cross-channel term dominates, on the adversarial random gradients the self-extension takes over, and the reconstruction never fabricates a colour-line that is not there. It is the correlation, not the number of survivors, that decides whether a guide can be trusted — a two-survivor pixel on decorrelated content is no better guided than a one-survivor pixel on a clean colour-line.

Filling holes with no survivor

Where every channel clips, no colour-line can be fit: the highlight’s fine detail and its exact peak are genuinely lost. But two things can still be salvaged from the surroundings, provided they are reconstructed jointly : the hole’s low-frequency magnitude curvature and its chrominance.

The naive move is to dome each channel independently: run the per-channel biharmonic self-inpaint of the confidence fallback, now unweighted, on all three channels. It fails in an instructive way. Because the channels clip at different radii (each has its own white level, and a near-neutral highlight crosses them in turn), each channel is domed from a rim at a different distance from the centre, and the three domes reach different heights. The last channel to clip saturates flat right at its rim, so it carries almost no inward slope and its dome barely rises, while the first channel to clip is domed across a wide radius and rises steeply. The core therefore drifts off-hue: a neutral sun blown out over an orange sky reconstructs as a yellow disk, because blue (the last to clip here) collapses while red over-recovers. Three correct-looking 1-D domes, one wrong colour.

The fix is to separate what is shared from what is not. Magnitude is common to all three channels (they are all bright because the same light overflowed them), so it is reconstructed once, as a single dome. Chrominance varies smoothly and is carried inward from the rim. Concretely, split the summed luminance $L_\text{sum} = R+G+B$ from the chrominance $\text{RGB}/L_\text{sum}$, reconstruct each by its proper operator, and recombine $\text{core}_c = L_{\text{dome}}\cdot (\text{RGB}/L)_c$.

The magnitude dome

The luminance $L_\text{sum}$ is domed over the all-clipped core by the biharmonic inpainting introduced in first principles : the solve $\Delta^2 L = 0$, anchored to the true valid rim of the core (the already-reconstructed partial-clip annulus that surrounds it). The operator itself is a revival : a 2021 design had tried gradient extension as a reverse-sign Laplacian (the diffuse or sharpen trick), but iterating a reverse diffusion is unstable and it was dropped as artifact-prone ; the rebuild brings the idea back in its stable, direct form, solved once on the summed luminance rather than iterated per channel. Filling the magnitude by plain diffusion would flatten it into a matte disk; the biharmonic solve instead continues the rim’s slope, so a large all-clipped core rises into a dome rather than collapsing to a plateau. Because all three channels ride on this one dome, none can collapse relative to the others — the failure above is structurally impossible. It cannot reach the exact peak (the steepest part of the gradient was clipped away, and the true summit is simply unobserved), but it recovers roughly $75$–$80\%$ of the clipped rise, as a smooth dome of the correct width. Solving a single scalar field rather than three also makes the biharmonic system a third the size.

A saturation floor

The guided fit and the biharmonic dome are both extrapolations, and an extrapolation can undershoot: a channel rebuilt from a single low survivor, or a dome continuing a rim whose inward slope was flattened by clipping, can come out below the level the channel actually reached. That is unphysical : as the problem established, the photosite well fills to a fixed capacity, so a clipped channel is known to sit at least at its clip level. Two floors enforce exactly that, and both are monotone : they only ever raise a value, so they cannot overshoot and cannot shift a hue that was already correct (a note of attribution : the floors, like the 9-point Laplacian, were already in the 2021 C implementation — the study’s contribution is only to have measured their worth, and the floors alone are a strict, uniform improvement on every scene) :

  • every clipped channel is floored at its own saturated reading;
  • the all-clipped core’s luminance dome is floored at the sum of the clip levels : every channel there is maxed, so the core is the brightest region, never a dip below its rim.

Cheap and physically unarguable, the floors remove the residual magenta-and-dark cast that the dome and diffusion alone can leave in a large all-clipped core (a blown sun disc), and they account for the improvement on the all-clipped correlated case reported below.

Chrominance, by diffusion

The chrominance $r = \text{RGB}/L_\text{sum}$ is diffused inward from the core rim : harmonic inpainting of the chrominance, $\Delta r = 0$ with $r$ fixed on the rim. Crucially the rim here is the reconstructed annulus immediately around the core, not the distant sky: as the surviving channels were guided back toward neutral approaching the highlight, the annulus is already near-neutral, so the diffused core comes out near-neutral too — the blown sun is repainted white, not the sky’s orange. (The earlier à-trous implementation instead pulled its chrominance from too far out and yellowed the core; both the reference and the current segmented reconstruction diffuse from the near annulus, as here.) The recombined $\text{core}_c = L_{\text{dome}}\cdot r_c$ is then a correctly-coloured dome.

The previous à-trous implementation realised this $\Delta r = 0$ solve not as one linear system but as an iterated relaxation: decomposing the ratios into wavelet bands, each detail band takes one explicit Euler step of

$$ \begin{align} r &\leftarrow r + \kappa \, \big( \Delta r - \lambda \, r \big)\\ \kappa &= \frac{1}{\texttt{B_SPLINE_TO_LAPLACIAN}} = \frac{\sigma_B^2}{2\sqrt{\pi}} \approx 0.31 \end{align} $$

with $\Delta$ the isotropic 9-point Laplacian $\mathbf{K}_{\text{iso}}$. The constant $\kappa$ rescales the à-trous detail band (a difference of B-splines) into a correctly-scaled continuous Laplacian and sets the effective diffusion time step.7 The rebuilt reconstruction solves $\Delta r = 0$ directly instead : a sparse solve in the prototype, conjugate gradient (an iterative solver that refines its answer step by step) in the C. But the relaxation view still exposes two things:

  • With $\lambda = 0$ it is the pure heat equation $\partial r / \partial t = \kappa \Delta r$ on the colour ratios : harmonic inpainting that fills the hole’s chrominance smoothly from its rim, the Dirichlet minimizer derived above (equivalent to the direct $\Delta r = 0$ solve, iterated).
  • The term $-\lambda\, r$, controlled by the solid_color parameter, is a first-order reaction damping the ratio detail toward zero. Its Euler–Lagrange equation is the screened-Poisson / modified-Helmholtz equation $\Delta r - \lambda\, r = 0$ : a diffusion held back by a restoring pull toward zero; larger $\lambda$ pulls the interior chrominance toward a flatter, more uniform “solid color” fill.

The diffusion is purely isotropic: texture should follow structure, but chrominance should fill without regard to direction, so a rotationally-symmetric smoother is what is wanted. The whole joint fill is gated to the all-clipped pixels, so it never disturbs a channel the guided step already recovered.

The saturation floors, as obstacles. One measurement survives even where all three channels are blown : each photosite’s saturated reading is a lower bound on its true value, so in ratio space the chrominance of a clipped channel can never drop below $c_{0,c} / L_\text{sum}$. Applied only at the very end — a hard $\max$ at the recombination — that bound printed an exactly-flat shelf at the clip level, ending in a gradient break, wherever the smooth fill under-predicted a channel near its own rim (measured on a real sunset’s blue channel : ten pixels flat at the clip value, then a slope discontinuity). The shipped reconstruction instead treats the floors as a constraint of the diffusion itself — an obstacle problem :

$$ \min_{r_c} \int_{\Omega} \nabla r_c^{\top} D\, \nabla r_c \;\mathrm{d}\Omega \qquad \text{subject to} \qquad r_c \;\geq\; \frac{c_{0,c}}{L_\text{sum}} \quad \text{on the clipped pixels,} $$

solved by projected relaxation : after every smoothing step, $r_c \leftarrow \max(r_c, c_{0,c}/L_\text{sum})$. Every neighbour weight of the discretization is nonnegative, so the projected scheme stays monotone and converges to the variational-inequality solution : the constraint’s influence spreads smoothly through the field — the fill lifts around the active set instead of being clamped pointwise — and the recombination then only applies a soft rounding (the same two-percent smooth-max as the coefficient-field floor) to a field that is already admissible. The obstacle rides the whole coarse-to-fine ladder (per-level cell obstacles) plus a short projected polish at full resolution, which also covers the direct-solve path — an exact sparse factorization cannot project mid-solve. A stronger variant, raising the obstacle by the rim’s measured outward slope extrapolated inward, was implemented, measured, and rejected : it fabricates structure where the extrapolation binds (the magenta sun’s error rose by 14 %) and changes nothing where the plain obstacle already holds.

image

The desperate all-clipped case: a bright sun blown past every channel’s (per-channel) white point, over an orange sky. The sensor records a flat disk that white balance RGB coefficients will turn magenta ; the previous method returns a dim, over-sized yellow disk with a hard edge (its per-channel fills drift off-hue and never dome); the corrected method rebuilds a neutral dome of the right size from one shared luminance dome plus rim-diffused chrominance.

Over the clipped core, RMSE $0.87 \to 0.27$ and SSIM $0.61 \to 0.95$; the fill’s chrominance moves from magenta $(0.37, 0.27, 0.36)$ to a near-neutral $(0.39, 0.33, 0.28)$, essentially the ground truth’s $(0.36, 0.33, 0.31)$ (the previous method lands at a yellow $(0.51, 0.34, 0.15)$).

Adicionando granulação

Como toque final, e somente onde a máscara está aberta, a reconstrução recebe ruído poissoniano de amplitude proporcional ao valor local ($\sigma_c = \texttt{noise_level}\cdot u_c$), dobrado para ser estritamente clareador e composto por $\alpha$. O ruído de disparo de fótons depende do sinal, então uma região perfeitamente lisa inserida em um quadro de ISO alto parece plástica; combinar a granulação faz o reparo desaparecer.2

O raio de reconstrução

Qual deve ser a largura da janela guiada mais grosseira, o raio de reconstrução $\rho$? É o único botão que troca alcance por localidade, e acontece que ele tem uma resposta limpa.

O raio de um buraco é a sua profundidade, não o seu tamanho. Para um buraco $\Omega$, seja $d(x)$ a distância de um pixel recortado $x$ ao pixel válido mais próximo. A quantidade que importa é o ponto mais profundo,

$$R=\max_{x\in\Omega} d(x)=\max\big(\text{transformada de distância de }\Omega\big)$$

não a caixa delimitadora. Um disco compacto e um traço fino, longo e diagonal podem compartilhar uma caixa delimitadora enquanto têm $R$ completamente diferentes: para o traço, $R$ é a sua meia-espessura, porque é essa a distância que o guia precisa alcançar para tocar dados válidos. Dimensionar o raio a partir da caixa delimitadora superaria maciçamente o alcance em buracos alongados ou diagonais.

Por que $\rho \approx R$ é ótimo. Dois erros competem à medida que $\rho$ cresce. Cobertura: uma janela de escala $\rho$ coloca peso $\sim e^{-d(x)^2/2\rho^2}$ sobre dados válidos, o que é desprezível até $\rho \gtrsim d(x)$; abaixo disso o ajuste é indeterminado e o pixel fica efetivamente sem reconstrução. Localidade: uma vez coberta, a linha de cor ajustada é uma média sobre a janela da verdadeira linha que varia espacialmente, então extrapolar a partir de amostras cada vez mais distantes adiciona um viés que cresce com $\rho$. O erro por pixel é, portanto,

$$E(x;\rho)\approx \underbrace{C_1\,e^{-d(x)^2/2\rho^2}}_{\text{cobertura}\ \downarrow}+\underbrace{C_2\,\rho}_{\text{localidade}\ \uparrow}$$

cujo mínimo fica em $\rho^*(x)\approx d(x)$: a menor janela que alcança dados válidos. Um único raio de região deve cobrir o seu pixel mais profundo, então $\rho=R$; a escada do grosseiro ao fino é então exatamente esse ótimo por pixel tornado contínuo: a escala mais grosseira $R$ serve o centro, cada escala mais fina serve a casca na sua própria profundidade.

O excesso de alcance é gratuito em qualidade, mas não em preço. No protótipo, varrer $\rho$ de $0.4R$ a $3R$ deixa tanto o RMSE médio quanto o do núcleo profundo planos, em cenas correlacionadas e descorrelacionadas igualmente. A razão é estrutural: a escada do grosseiro ao fino sobrescreve cada valor grosseiro (suavizado em excesso) com a escala mais fina que ainda alcança o pixel, então o raio define apenas a cobertura, nunca o valor final de um pixel. O cálculo, no entanto, não é plano (ele cresce tanto com a área preenchida quanto com o número de escalas), então a razão qualidade-por-custo cai monotonicamente com $\rho$:

$\rho/R$RMSE médioRMSE do núcleo profundocusto relativoqualidade / preço
0.40.0830.1410.431.00
1.00.0830.1411.000.43
2.00.0830.1412.390.18
3.00.0830.1414.430.10

O ponto ideal é por pixel, e a implementação o entrega. Como a qualidade é plana acima da profundidade e o custo não é, a melhor qualidade/preço é a menor janela que ainda cobre cada pixel, isto é, $\rho(x)=d(x)$ por pixel, nunca um máximo global. A escada C realizava isso diretamente: um ladrilho mesclado tomava o maior raio $R$ do grupo como sua escala mais grosseira (para que o pixel mais profundo fosse coberto), e cada pixel era então limitado a escalas não mais grosseiras do que $\sim2\,d(x)$, de modo que um pixel raso (um pequeno buraco, ou a borda de um grande) nunca pagava pelas escalas grosseiras de que só o centro precisa. A transformada de distância é calculada uma vez para o quadro inteiro e comanda o raio por região. A única regra firme é a assimetria: nunca ir abaixo da profundidade (a cobertura colapsa), e nunca muito acima dela (puro cálculo desperdiçado). O método distribuído mantém a conclusão em uma forma mais simples: uma janela de ajuste dimensionada ao raio da região, $\sigma = \operatorname{clip}(r/6,\, 8,\, 64)$ px, sem restar nenhum controle por pixel a fazer.

Implementação e otimizações

Existem duas implementações: a referência em NumPy publicada ao lado deste artigo, e o código de produção em C / OpenCL que é distribuído no Ansel. Elas são comparadas uma com a outra como parte do protocolo de validação, e divergem apenas onde a velocidade exige.

O código de produção em C

O protótipo que acompanha este artigo é a referência: ele favorece clareza e exatidão sobre velocidade (soluções esparsas diretas sobre a imagem inteira, uma escada gaussiana fixa, um passo separado de variância, um ajuste de linha de cor por par (canal, guia)). O código que de fato é distribuído no Ansel tem de reconstruir um sol recortado dentro de um raw de mais de 20 Mpx enquanto o usuário espera, então ele diverge da referência de algumas maneiras deliberadas. Cada compromisso abaixo é ajustado para conteúdo correlacionado, o caso avassaladoramente comum em imagens naturais, e aceita uma pequena perda em conteúdo sintético descorrelacionado, que é raro na prática. Cada desvio é reversível no código-fonte. Uma nota histórica: este trabalho de otimização foi feito no C da era da escada, cujo núcleo de reconstrução o método distribuído substitui. A segmentação, o preenchimento, o solucionador esparso e o maquinário de estatísticas compartilhadas passam adiante literalmente; os itens específicos da escada (a escada de escalas por buraco, o portão de profundidade por pixel, a escolha de guia restrita a pares) sobrevivem apenas como princípios, dentro da janela única de ajuste e do preenchimento de coeficientes do método distribuído.

  • Segmentar e reconstruir apenas as vizinhanças recortadas, em resolução plena. A referência roda sobre o quadro inteiro. O código distribuído preenche por inundação a máscara recortada em regiões conectadas, preenche cada uma pelo seu próprio raio de reconstrução ($1.25\times$ o raio, limitado entre $8$ e $256$ px — quanto maior o buraco, mais longe ele deve alcançar por dados válidos), mescla regiões cujas caixas preenchidas se sobrepõem para que seus preenchimentos nunca se encontrem como uma emenda, e roda o pipeline completo apenas dentro de cada caixa preenchida. Custo: nenhum — os pixels intocados já estavam corretos. Ganho: o trabalho escala com a área recortada, não com o sensor.

  • Uma Cholesky esparsa autônoma fechou a lacuna do solucionador. A referência preenche o domo de magnitude com uma solução esparsa direta (scipy.spsolve); um solucionador iterativo (gradiente conjugado sobre $\Delta^2$) estagna em precisão simples na escala do raw (seu número de condição, o fator pelo qual o sistema amplifica erros de arredondamento, cresce como a quarta potência do tamanho da região), e uma fatoração densa é $O(N^3)$, então o C primeiro distribuiu uma Cholesky densa (a fatoração exata padrão de um sistema simétrico em metades triangulares, calculada uma vez e reutilizada) sobre uma grade engrossada para no máximo 2000 incógnitas. Sendo o domo de baixa frequência, a solução grosseira era exata onde importava — exceto em núcleos enormes totalmente estourados, onde o próprio engrossamento se tornava o gargalo de exatidão (o caso de teste do sol magenta melhorou 21 % em RMSE no dia em que o limite foi removido). O C agora carrega sua própria Cholesky esparsa simétrica positiva definida (SPD), uma fatoração $LL^T$ ascendente em precisão dupla, ordenada por dissecção aninhada geométrica: as incógnitas são pontos de grade 2D, então a bissecção recursiva de coordenadas entrega a qualidade de redução de preenchimento que normalmente requer uma implementação de grau mínimo aproximado (AMD), em algumas dezenas de linhas e sem dependência externa. A grade do domo é quatro vezes mais fina (8 192 incógnitas, o que satura mensuravelmente o ganho de qualidade), e a difusão de crominância do núcleo totalmente recortado fatora uma vez para seus três lados direitos de canal quando o núcleo é pequeno; o gradiente conjugado paralelo bate mensuravelmente a fatoração serial nos grandes, então ele os mantém. Custo: nenhum líquido (as soluções totalmente recortadas ficaram mais rápidas). Ganho: a última divergência sistemática entre C e protótipo eliminada, e junto com ela a exatidão do núcleo enorme.

  • Uma escada gaussiana por buraco dimensionada ao raio de reconstrução. A referência usa uma escada de raio fixo ($40, 24, \dots, 2$ px). O C otimizado da escada dimensionava a escala mais grosseira de cada região ao raio de reconstrução daquela região e reduzia pela metade por fatores de $2\times$ até $\sim2$ px. Isso merece seu próprio tratamento: veja O raio de reconstrução abaixo.

  • Estatísticas janeladas compartilhadas: uma aceleração gratuita. O ajuste da linha de cor precisa, por par de canais, das médias, variâncias e covariância janeladas. Reuni-las por (canal, guia) custa doze borramentos por escala; reuni-las por par de canais produz os ajustes de ambos os canais a partir dos mesmos dois borramentos: seis borramentos, e matematicamente idêntico. Só isso praticamente reduziu o tempo de execução pela metade.

  • Variância restrita a pares para seleção de guia: um custo pequeno e contido. A referência decide em qual dos dois guias confiar a partir da variância janelada de canal único de cada guia, um passo separado de três borramentos por escala. O C otimizado da escada reutilizou a variância restrita a pares que ele já calculava para o ajuste e descartou aquele passo inteiramente (nove borramentos por escala reduzidos a seis). Isso mudou apenas qual guia vence, e apenas onde os dois discordam, isto é, conteúdo descorrelacionado. Custo: em uma cena sintética totalmente descorrelacionada, o RMSE de recorte único sobe de $0.043$ para $0.047$ e o SSIM cai de $0.964$ para $0.960$; em cenas correlacionadas, e em recorte de dois ou três canais, fica dentro do ruído. Ganho: um terço dos borramentos restantes removido.

  • O autodomo por canal e o regularizador de emenda estão desabilitados no código distribuído. Ambos suavizam cada canal RGB independentemente. Em uma alta luz âmbar/laranja saturada real (onde o $R^2$ da linha de cor cai devido à não linearidade do recorte e é mal interpretado como “descorrelacionado”) isso desvia o verde para longe do vermelho e do azul e torna a reconstrução magenta. A referência os mantém: eles removem artefatos de conteúdo genuinamente descorrelacionado, que as cenas sintéticas exercitam intensamente e que fotografias reais raramente têm (desabilitá-los custa à referência $\sim0.01$–$0.02$ de RMSE na cena aleatória, essencialmente nada em conteúdo correlacionado). A correção limpa é uma versão acoplada em matiz (suavizando a luminância e uma crominância compartilhada em vez de três canais independentes), e ela desde então foi construída: no pipeline de campo de coeficientes final o autodomo retorna como o recurso de reserva limitado por profundidade, acoplado em matiz exatamente dessa maneira (um domo de luminância biarmônico compartilhado multiplicado por uma crominância preenchida harmonicamente), de modo que o recurso de reserva não pode desviar o matiz por construção. O regularizador de emenda foi aposentado definitivamente — aquele pipeline não tem mais transferências entre estimadores a alisar.

Dois componentes da implementação em C de 2021 que a tradução do protótipo havia silenciosamente descartado foram mesclados de volta à referência, como genuínos requisitos do método em vez de compromissos de velocidade, então eles já não são diferenças: o laplaciano de 9 pontos invariante à rotação (o operador que este artigo deriva; a referência usava a cruz simples de 5 pontos antes567) e os pisos de saturação (abaixo). Ambos melhoram o caso mais difícil e mais relevante (o núcleo totalmente recortado de uma cena correlacionada), reduzindo seu RMSE de $0.065$ para $0.062$ e elevando o SSIM de $0.959$ para $0.964$, e são neutros em outros lugares.

Em conjunto, em um sol recortado a contagem de borramentos do filtro guiado cai de quinze para seis por escala e a reconstrução de pré-visualização cai de dezenas de segundos para poucos. As diferenças que restam são velocidade e arquitetura (segmentação, solucionadores densos, o raio por buraco, estatísticas compartilhadas), mais a variância de guia restrita a pares e o refinamento por canal desabilitado acima; a matemática central da reconstrução é compartilhada com a referência.

Mais um compromisso medido foi fechado cedo: a reconstrução é marcadamente melhor rodada em resolução plena. O módulo de 2021 resolvia em uma cópia de um quarto da resolução por velocidade, mas essa subamostragem borra a reconstrução e, mesclada de volta contra o original nítido, é a fonte dominante de erro de borda: rodar em resolução plena reduziu o RMSE de fronteira em cerca de 30%, então a reconstrução funciona em resolução plena. A subamostragem é um compromisso velocidade/qualidade, não um almoço grátis.

A referência em Python

O espelho em NumPy (validate.py, fix_prototype.py) implementa cada estágio do algoritmo distribuído na mesma ordem, e comparar sua saída contra as exportações em C faz parte do protocolo de validação. Alguns componentes são deliberadamente melhores em Python do que em C, ou o C deliberadamente troca exatidão por velocidade:

  • Soluções esparsas diretas exatas. Os domos biarmônicos do protótipo e sua solução de crominância em forma de divergência são fatorações scipy.sparse, exatas até a precisão de máquina (seu preenchimento de núcleo conjunto Poisson-blindado, no entanto, é uma relaxação Jacobi fixa de 400 varreduras — nesse único estágio a fatoração direta do C é a mais exata das duas). O C agora carrega sua própria Cholesky esparsa (veja Implementação e otimizações acima) e casa com as soluções de domo e de crominância de pequeno núcleo do protótipo; o que ainda difere é deliberado: a difusão de crominância de núcleos totalmente recortados grandes roda o gradiente conjugado paralelo (medido como mais rápido que uma fatoração serial ali, sem diferença de qualidade mensurável), o preenchimento de coeficientes permanece o Jacobi do grosseiro ao fino descrito acima — com a pirâmide adaptativa do C (passo base $\sigma/4$ limitado a 8 px, reduzido pela metade até uma escala mais grosseira de $\le 8$ células, 100 varreduras por nível) contra os fixos sete níveis de resolução plena do protótipo — e o preenchimento do protótipo é isotrópico — o direcionamento adaptativo à variância do transporte de coeficientes do C (passo 3) ainda não tem espelho em Python, então a concordância entre C e protótipo é verificada tendo em mente a contribuição do direcionamento. Benefício das soluções exatas do protótipo: elas são o árbitro quando a saída do C parece errada.
  • Janelas gaussianas por FFT vs. IIR recursiva. O protótipo calcula momentos janelados com convoluções por transformada rápida de Fourier (FFT): exatas, determinísticas. O C usa a gaussiana recursiva (resposta ao impulso infinita, IIR) cujo custo é independente de $\sigma$ — mas cuja implementação paralela não é determinística de execução para execução: o arredondamento nas fronteiras de bloco oscila no último dígito de float, o que é inofensivo em si mas semeou um memorável heisenbug (abaixo).
  • Entrada da estimativa do joelho. O protótipo estima o rolamento a partir da cena RGB verdadeira em resolução plena; o C, a partir da CFA agrupada em quádruplas a $\le 1.5$ Mpx. Custo medido: nenhum (a estimativa cega do C ainda casa com o joelho analítico até $10^{-3}$), enquanto o agrupamento em quádruplas é o que torna a estimativa do lado da CFA possível de todo.
  • Quadro inteiro vs. por região. O espelho processa quadros inteiros; o C reúne cada região recortada em um buffer preenchido e a reconstrói independentemente, o que limita a memória e permite que regiões paralelizem, ao preço de uma normalização por região.
  • Refinamentos distribuídos que o espelho ainda não alcançou. Três rodadas tardias em C não têm contrapartida em Python ainda: o maquinário de obstáculos (relaxação projetada por nível, o polimento em resolução plena e o piso suave de remontagem — o espelho ainda aplica o aposentado $\max(u, c_0)$ rígido na remontagem), a sobreposição de banda do limiar de detecção para canais com o joelho engajado (vale $5\times$ na cena de rolamento, então Py-vs-C é estruturalmente incomparável ali), e a validade binária estritamente por pixel do C (o espelho deriva sua máscara por um voto de maioria 5×5, uma dilatação geométrica de $\sim 2$ px). Essas são as lacunas conhecidas de conteúdo por trás das maiores divergências por cena.

Depois que cada estágio foi espelhado, a discordância residual entre o código de produção em C e a referência em Python (medida como a diferença quadrática média de suas imagens de saída dentro das zonas recortadas) é de $0.009$–$0.089$ ao longo do banco. Seu piso é a única diferença de entrada que não pode ser removida — o Python ajusta sobre a cena RGB verdadeira, enquanto o C ajusta sobre a mesma cena vista através do mosaico do sensor e de uma interpolação bilinear (cerca de $0.02$ de distância dentro da banda próxima ao recorte) — e seu teto é a lista de refinamentos distribuídos acima, que domina nas cenas de rolamento e de oclusão. Longe dessas duas cenas, as implementações concordam bem dentro do erro que qualquer uma comete contra a verdade de referência; nelas, o maquinário extra do C é precisamente o que a divergência mede.

Parâmetros do usuário

Há deliberadamente quase nada a definir: o algoritmo é autoconfigurável. Toda quantidade da qual a reconstrução depende ou é derivada uma vez e congelada, ou medida a partir da própria imagem em tempo de execução — a escala de ajuste a partir do próprio raio de reconstrução de cada região, o rolamento do sensor a partir das próprias linhas de cor da imagem, a confiança da âncora a partir da qualidade de ajuste medida, o direcionamento do transporte a partir da estrutura de guia medida, o limiar de borda $k$ calibrado uma vez no banco de verdade de referência. Os controles iterations e diameter do modo de 2021 sumiram da interface deste modo: não resta nenhuma convergência ajustável pelo usuário a configurar errado, e nenhuma maneira de sacrificar qualidade por acidente.

O que resta é uma segurança e dois gostos. O clipping threshold escala os limiares de saturação que definem a zona (veja o botão de visualização abaixo — quase nunca precisa ser movido); o noise level define a granulação reinjetada sobre a reconstrução para que ela combine com a textura de fótons ao redor; e inpaint a flat color define a atração em direção a uma cor uniforme nos núcleos totalmente recortados onde nada sobreviveu — uma escolha estilística sobre zonas que não carregam nenhuma informação.

Problemas descobertos

A limitação estrutural do método é herdada e aceita: um céu azul estourado visto através de folhas verdes é recuperado verde, porque onde nenhum canal sobrevive a reconstrução só pode propagar a cor vizinha — ele foi projetado para os recortes inevitáveis ao redor de fontes de luz e reflexos especulares, cujos halos brilhantes carregam a informação de que ele se alimenta. Outras situações ainda pedem proteger as realces na captura e elevar a exposição na pós-produção, o que a relação sinal-ruído dos sensores modernos permite confortavelmente.

Duas ressalvas medidas completam o quadro honesto. Na cena de banco de oclusão, a segmentação do Darktable ajustada alcança uma pontuação de similaridade estrutural mais alta do que a transposição harmônica (0.98 contra 0.96) apesar de valores 15 % mais distantes da verdade: magnitude e estrutura são erros diferentes, e um preenchimento errado mas suave pode parecer mais limpo do que um verdadeiro. E a paridade de dispositivos do pipeline de float é ao nível de tolerância, não ao nível de bit: depois de centrar a acumulação de momentos (passo 3), a diferença residual entre CPU e GPU na cena de banco mais profunda fica abaixo de $10^{-3}$ de RMSE, limitada por reduções paralelas de ordem não determinística.

Resultados

O banco

Todo número abaixo vem de um protocolo. Seis cenas sintéticas com verdade de referência conhecida — bolas especulares RGB, um sol magenta, gradientes correlacionados (um campo de luminância multiplicado por uma crominância suave, como imagens naturais se comportam), gradientes aleatórios independentes (nenhuma linha de cor, o caso adversário), um céu tipo PK1 com um rolamento de sensor analítico, e um sol cruzado por um obstáculo — são escritas como arquivos DNG Bayer, revelados pelos pipelines de produção reais, e as exportações são pontuadas contra a verdade de referência em RGB do sensor com balanço de branco (uma matriz de calibração 3×3 completa ajustada sobre pixels válidos estritamente lineares mapeia cada exportação de volta ao espaço do sensor, então pipelines com matrizes diferentes chegam às mesmas unidades). Todo resultado do Ansel nesta seção — ambos os modos, cada tabela, cada figura — é produzido pela implementação em C distribuída através do ansel-cli, não pelo protótipo de pesquisa em Python: as duas implementações divergem deliberadamente (solucionadores, resoluções e compromissos de estágio diferentes, veja o código de produção em C), de 0.009 a 0.089 de RMSE dentro das zonas recortadas ao longo deste banco, então seus números nunca devem ser misturados. Todo o banco reproduz a partir do anexo.

As métricas do algoritmo distribuído abaixo são calculadas dentro da área recortada apenas. RMSE é a raiz do erro quadrático médio contra a verdade de referência, em unidades de sensor normalizadas de modo que o nível de recorte seja 1: ela mede quão distantes, em média, os valores reconstruídos estão dos verdadeiros. SSIM é o índice de similaridade estrutural, uma pontuação perceptual comparando médias, contrastes e correlações locais (1 significa visualmente idêntico). Energia de borda é o nosso detector de emenda: a energia do gradiente de crominância dentro da zona, relativa a um anel logo fora dela, e seu alvo é a própria pontuação da verdade de referência, não zero, porque conteúdo real tem textura e uma reconstrução suave em excesso é tão errada quanto uma com emendas:

casoRMSESSIMenergia de borda (alvo GT)
bolas RGB0.03510.9950.41 (0.47)
sol magenta0.32760.9520.31 (0.50)
correlacionado0.03980.9731.15 (1.14)
aleatório0.04530.9650.85 (1.02)
pk1synth0.00531.0000.64 (0.69)
PK1 (natural)0.49
DSC00078 (natural)0.73
IMG_3129 (natural)0.39

Os quatro métodos

Os dois modos do Darktable são descritos com as outras correções simples em a seção do panorama; lidos através do arcabouço deste artigo, inpaint opposed é uma linha de cor com a inclinação fixada em um (no espaço de raiz cúbica) e um único intercepto global — onde a transposição harmônica ajusta tanto inclinação quanto intercepto por janela local e difunde esses coeficientes por toda a zona, o opposed confia em uma relação fixa em toda parte, o que é por que ele é quase gratuito e por que falha sob iluminação mista — e segmentation based eleva isso a uma crominância representativa por segmento, ainda plana onde nossos campos difundidos variam suavemente.

Ambos os métodos respondem à pergunta “que cor esta área deveria ter”; nenhum ajusta um modelo de como o canal ausente se relaciona com os sobreviventes, que é o que permite ao nosso método recuperar estrutura e magnitude por pixel com uma confiança medida. As linhas abaixo põem números sobre essa diferença, nas mesmas cenas sintéticas e verdades de referência que todo o resto nesta seção.

Raiz do erro quadrático médio (menor é melhor) e similaridade estrutural (maior é melhor), medidas dentro da zona recortada contra a verdade de referência, após calibração de exposição por imagem; cada método exportado como TIFF linear de 32 bits (Darktable 5.x a montante para as duas primeiras colunas, Ansel para as duas últimas), pontuado pelo mesmo validate.py que toda outra tabela nesta página. Duas precauções tornam as colunas de fato comparáveis. Primeiro, os pipelines são neutralizados à mesma base — interpolação cromática mais reconstrução de realces, nada mais: o fluxo de trabalho referente à cena padrão de ambos os aplicativos insere silenciosamente um levantamento de exposição, um mixer de canais de adaptação cromática e uma curva de tom sigmoide ao redor de sua reconstrução, o que corromperia a medição com diferenças de renderização, então ele é forçado a workflow=none (a exportação do Ansel já é essa base). Segundo, cada concorrente roda seus melhores parâmetros por cena, encontrados por uma varredura em grade sobre seus controles (limiar de recorte para opposed; candidatura, combinação, modo de reconstrução e força para segmentation; iterações e preenchimento de cor plana para os guided laplacians), enquanto a transposição harmônica roda seus padrões de fábrica em toda parte. Reproduza com dt_compare.py (a comparação) e tune_methods.py (a varredura) do repositório de pesquisa:

cenaDarktable opposedDarktable segmentationguided laplacianstransposição harmônica
balls (bolas especulares)0.242 / 0.8140.233 / 0.8310.334 / 0.7440.035 / 0.995
magentasun (sol magenta)0.924 / 0.7070.807 / 0.7670.809 / 0.6780.328 / 0.952
correlated (gradientes correlacionados)0.107 / 0.8720.077 / 0.9070.130 / 0.8500.040 / 0.973
random (gradientes independentes)0.089 / 0.9110.080 / 0.9200.138 / 0.8620.045 / 0.965
pk1synth (céu tipo PK1 (rolamento analítico))0.130 / 0.9850.130 / 0.9850.108 / 0.9930.0053 / 1.000
occluded (sol ocluído)0.099 / 0.9820.086 / 0.9800.171 / 0.9300.069 / 0.987

O padrão é consistente com os mecanismos. Mesmo com os pipelines igualados e cada concorrente em seu melhor por cena, a transposição harmônica lidera em RMSE em todas as cenas — por 20 % onde a oclusão favorece as suposições do modo de segmentação, por um fator de 2 a 7 nos casos gerais, por um fator de quase 50 no céu de rolamento (onde a sobreposição de banda abaixo permite ao modelo reconstruir a inclinação que o sensor comprimiu): os modos do Darktable recuperam uma cor plausível mas não a magnitude (sua inclinação unitária fixa não pode elevar um canal acima do que a média oposta fornece), e sua crominância por segmento ou global achata a textura. O modo de segmentação de fato avança à frente do opposed simples uma vez que seu passo de reconstrução é efetivamente habilitado (a varredura encontrou reconstrução + força total ótima em metade das cenas). A contraparte visual de cada número, uma cena por galeria, todas as seis versões através da mesma transformação de exibição:

balls — bolas especulares:

magentasun — sol magenta:

correlated — gradientes correlacionados:

random — gradientes independentes:

pk1synth — céu tipo PK1 (rolamento analítico):

occluded — sol ocluído:

A varredura completa do banco de validação (entrada recortada, reconstrução, mapa de bordas de crominância ampliado, e verdade de referência onde ela existe), mais os perfis de domo por canal, é mostrada abaixo. Os gráficos de perfil são lidos como segue: cada um toma a única linha da imagem que cruza o pixel recortado mais profundo (recortada ao vão recortado daquela linha) e plota os valores dos canais ao longo dela, um painel por canal, com a linha cinza pontilhada no nível de recorte. Eles expõem exatamente o que uma miniatura esconde: a forma da seção transversal do domo reconstruído, suas inclinações onde ele encontra a borda válida, quão acima do nível de recorte a recuperação alcança, e qualquer sobredisparo ou ponto plano — comparados, nas cenas sintéticas, contra o próprio perfil da verdade de referência. Todos os perfis, imagens naturais incluídas, são plotados em RGB do sensor com balanço de branco, por fotossítio (os perfis de imagem natural leem a própria CFA, com a interpolação cromática definida como passagem direta) — o próprio plano de trabalho do módulo, onde o invariante do piso de saturação (um canal recortado nunca termina abaixo de seu valor saturado) é diretamente visível. Nenhum estágio posterior tem essa propriedade: os elementos negativos fora da diagonal da matriz de cor permitem que um canal reconstruído legitimamente cruze abaixo de seu platô de recorte (projeção de gamut, não uma violação de piso), a interpolação cromática inventa dois dos três canais em cada pixel e pode subdisparar nas bordas íngremes da reconstrução, e qualquer reamostrador adiciona seu próprio subdisparo de Mitchell. Um recurso dos perfis naturais merece um aviso: estruturas escuras cruzando a zona estourada (cordame, galhos, mastros) são dados válidos medidos — a reconstrução escreve apenas fotossítios recortados, então o perfil mergulha através delas em vez de abobadar sobre elas. Uma linha que cruza duas cordas retroiluminadas mostra dois lóbulos, e isso é a cena, não uma falha em reconstruir:

image
Bolas RGB: recortado, reconstruído, bordas ×8, verdade de referência, bordas ×8.
Bolas RGB: valores dos canais ao longo da linha através do pixel recortado mais profundo; cinza pontilhado = nível de recorte.
image
Sol magenta (núcleo totalmente recortado).
Sol magenta: perfil da linha através do pixel recortado mais profundo, por canal; a seção transversal do domo do núcleo totalmente recortado.
image
Gradientes correlacionados.
Gradientes correlacionados: perfil da linha através do pixel recortado mais profundo, por canal.
image
Gradientes aleatórios (descorrelacionados), o pior caso reconhecido.
Gradientes aleatórios (descorrelacionados): perfil da linha através do pixel recortado mais profundo, por canal.
image
pk1synth: a réplica sintética do céu estourado PK1, com um rolamento de sensor real.
pk1synth: perfil da linha através do pixel recortado mais profundo, por canal; note a recuperação acima do nível de recorte pontilhado.
image
PK1: o céu de pôr do sol estourado real que motivou a reconstrução.
PK1: perfil da linha através do pixel recortado mais profundo, por canal (sem verdade de referência: fotografia real).
image
DSC00078: brilho quente de pôr do sol, vigilância de regressão.
DSC00078: perfil da linha através do pixel recortado mais profundo, por canal (fotografia real).
image
IMG_3129: disco solar com um núcleo totalmente recortado.
IMG_3129: perfil da linha através do pixel recortado mais profundo, por canal (fotografia real).

Desempenho

Uma reconstrução que é mensuravelmente melhor mas inutilizavelmente lenta não seria distribuída. Esta seção fecha a história com números: como o novo método escala, o que portá-lo para o processador gráfico (GPU, via OpenCL) rendeu, e como os métodos antigo e novo se comparam na mesma máquina e nas mesmas imagens. O protocolo por trás de cada número abaixo é o mesmo: cada cronometragem é uma exportação completa através do ansel-cli, tomada como o mínimo de tempo de relógio de três execuções em uma máquina de resto ociosa, com cronometragens internas ao módulo lidas a partir do rastreamento de desempenho embutido.

Linear na área preenchida

Perguntar “quão rápida é a reconstrução por megapixel de imagem” é a pergunta errada: uma imagem sem recorte custa quase nada, e o mesmo sol estourado custa o mesmo quer o quadro ao redor dele seja de 12 ou 60 megapixels. A variável certa cai da estrutura de custo. Todo estágio pesado do pipeline distribuído é linear na área da região preenchida — a extensão da zona recortada mais duas vezes seu raio de reconstrução, ao quadrado — com constantes independentes da escala de ajuste: os momentos janelados usam filtros gaussianos recursivos cujo custo não depende do tamanho da janela, a difusão de coeficientes roda um orçamento fixo de cem varreduras por nível de pirâmide sobre uma grade base que encolhe à medida que a escala de ajuste cresce (com a pirâmide profunda o bastante para que sua grade mais grosseira seja trivialmente pequena), e a solução direta do domo de luminância é limitada a um número constante de incógnitas (8.192, em uma grade decimada) independentemente do buraco. E como o raio de reconstrução é o ponto mais profundo do buraco, o preenchimento não é um parâmetro livre: um buraco circular de raio $r$ precisa de uma região preenchida de aproximadamente $(4r)^2$ — dezesseis vezes a própria caixa delimitadora do buraco (a implementação preenche por $1.25\,r$ — o alcance mínimo mais uma margem de segurança de $25\,\%$ — limitado entre $8$ px, para que pequenas partículas ainda vejam contexto, e um teto de custo de $256$ px, para que os buracos mais profundos cresçam ligeiramente mais devagar do que esta lei). Dobrar o diâmetro de uma alta luz quadruplica seu custo, não porque o algoritmo seja superlinear, mas porque a reconstrução honestamente precisa ver tanto mais contexto.

O banco (make_perf_bench.py, reproduzível) consiste em raws sintéticos com um buraco recortado circular de raio crescente, em duas famílias que exercitam os dois principais caminhos de código — um único canal recortado (o campo de coeficientes faz o trabalho) e todos os três canais recortados (o núcleo conjunto faz) — cronometrados através do pipeline de linha de comando de produção, com cada região das três imagens de teste reais sobreposta como pontos medidos. A máquina é um Intel Xeon E3-1505M v6, 4 núcleos / 8 threads a 3.0 GHz.

Tempo de execução contra a área da região preenchida (log-log). Ambas as famílias sintéticas seguem a referência de inclinação 1 por mais de uma década; as regiões das imagens reais — de uma lasca de 0,1 Mpx ao céu de 15,8 Mpx do PK1 — se dispersam na mesma faixa.
A amplificação do preenchimento: a região que o algoritmo deve processar cresce como (4r)² para um buraco de raio r, porque o raio de reconstrução é igual à profundidade do buraco.
famíliaraio do buraco (px)região (Mpx)campo de coeficientes + domo (ms)núcleo conjunto (ms)crominância (ms)total (ms)ms / Mpx
canal único560.1013501136~1360
canal único1280.50781584~170
canal único2561.30209112222~170
canal único3842.00358117376~190
totalmente recortado560.10829930211~2110
totalmente recortado1280.5013790221448~900
totalmente recortado2561.3038422213531959~1510
totalmente recortado3842.0086336523753603~1800
PK1, céu estourado (real)6.1026894602753~450
DSC00078, maior região (real)2.80119134119433475~1240
IMG_3129, sol (real)0.506604435739~1480

Os números são mínimos por execução ao longo de execuções repetidas do pipeline de produção completo. Lendo isso: acima de aproximadamente meio megapixel o caminho de canal único mantém 170–190 ms por megapixel de região preenchida e o caminho totalmente recortado 900–1 800, ao longo de uma década de tamanhos de região — a afirmação de linearidade, verificada; abaixo disso, um piso fixo por região de algumas centenas de milissegundos (estatísticas janeladas em seus tamanhos mínimos) domina e a leitura por megapixel perde significado. (A constante totalmente recortada inclui o polimento de obstáculo do piso da crominância do núcleo — veja o algoritmo — cujo custo o portão de atividade do polimento agora dispensa onde quer que o piso de saturação não possa se aplicar; as correções de correção documentadas nos resultados compraram sua qualidade com trabalho honesto, e as rodadas de otimização retomaram o que puderam sem tocar na saída.) Dois ganhos estruturais chegaram desde a primeira versão desta seção. O solucionador esparso direto removeu os gargalos do domo e da crominância de pequeno núcleo (a coluna do núcleo conjunto atinge o pico em 0,3 s onde a Cholesky densa de float gastava segundos), e a solução direta em forma de divergência reduziu a crominância direcionada por estrutura a milissegundos onde quer que o núcleo totalmente recortado caiba no seu teto de fatoração. E corrigir o limiar de semente da segmentação (ele fazia as regiões crescerem por resíduo de float de borramento em caixa) encolheu as próprias regiões preenchidas: o céu estourado do PK1, antes um retângulo preenchido de 15,8 Mpx, é agora os honestos 6,1 Mpx — toda a sua reconstrução leva 1,3 s. O que domina agora é o estágio do campo de coeficientes (momentos janelados e preenchimentos harmônicos, limitados por borramento) — que é exatamente onde o porte OpenCL descrito abaixo começa.

Tempo de relógio, antigo versus novo

A comparação que os fotógrafos de fato sentem é o tempo total de exportação, modo antigo contra novo, na mesma máquina e nos mesmos arquivos raw, sob o protocolo declarado acima. Três casos naturais: PK1 (um arquivo Bayer de 36 megapixels cujo céu estourado é uma região de 6 megapixels — o pior caso para o qual o novo método foi construído), X-H1 (um arquivo Fujifilm X-Trans de 24 megapixels cujas realces se fragmentam em 99 regiões majoritariamente minúsculas), DSC00078 (um pôr do sol Bayer de 24 megapixels), e os dois extremos sintéticos do banco. Vale lembrar aqui que os guided laplacians são reduzidos por um fator de 4, isto é, um dezesseis avos da resolução original, enquanto a transposição harmônica roda em resolução plena. (Estes tempos de relógio foram remedidos após toda a campanha de otimização abaixo, em uma sessão; a máquina carregava um trabalho de fundo constante em um núcleo, que os mínimos por célula sobre três execuções em sua maioria absorvem.)

exportação completa, tempo de relógioguided laplacians, processadortransposição harmônica, processadorguided laplacians, placa gráficatransposição harmônica, placa gráfica
PK1 (Bayer, uma grande mancha)14.3 s11.4 s10.4 s9.8 s
X-H1 (X-Trans, 99 regiões)9.3 s11.5 s6.9 s9.4 s
DSC00078 (pôr do sol Bayer)16.6 s14.4 s11.4 s12.5 s
occluded (sintético pequeno)0.9 s0.9 s1.2 s1.3 s
allclip-384 (grande disco totalmente recortado)2.2 s5.5 s1.9 s3.3 s

A manchete honesta é que os ganhos de qualidade documentados na seção de validação agora vêm a um tempo de relógio comparável ou melhor em quase toda parte — o novo método vence a coluna do processador sem contestação nos arquivos naturais, e apenas o disco totalmente recortado ainda paga um verdadeiro prêmio — e o caminho até essa frase é instrutivo, porque a primeira compilação de produção do transporte anisotrópico rodava o dobro desses números onde quer que as realces se fragmentem. Perfilar o arquivo X-Trans de 99 regiões mostrou que o custo não era aritmético: a relaxação lançava uma nova equipe de threads a cada varredura (dezenas de milhares de lançamentos de trabalho de microssegundos por imagem), os pesos de direcionamento eram recalculados a cada varredura embora o tensor seja fixo por nível, e cada plano de coeficientes rodava seu próprio preenchimento — própria pirâmide de máscara, próprio tensor — embora os planos compartilhem ambos. Uma região paralela por relaxação, pesos pré-calculados uma vez por nível, e os planos de máscara compartilhada fundidos em um único preenchimento levaram o tempo de relógio do processador daquele arquivo de 18,3 s para 9,8 s — o próprio direcionamento anisotrópico é essencialmente gratuito no processador. As correções de viés de borda e de obstáculo de piso documentadas nos resultados então reinvestiram parte desse ganho em trabalho honesto (a validade binária amplia os buracos verdadeiros; o polimento de obstáculo varre cada núcleo totalmente recortado), e uma rodada posterior recuperou parte dele (os borramentos de momento empacotados de quatro em quatro, o polimento condicionado a se seu piso pode se aplicar de todo), chegando aos 11,5 s da tabela. O que resta é estrutural. No processador o novo método vence onde seu projeto compensa — no caso da grande mancha seu trabalho é proporcional à área recortada (cada região resolvida uma vez, exatamente) onde o método antigo itera uma difusão em cada escala de pirâmide da imagem inteira — e seus tempos de relógio ainda incluem um estágio que o método antigo simplesmente não tem: a medição e inversão do rolamento de sensor (0,7 a 1,2 s sozinho nesses arquivos) — com ele, o novo método agora vence a coluna do processador em todos os três arquivos naturais. Na placa gráfica o método antigo mantém uma vantagem em cenas fragmentadas: um grande kernel uniforme por escala é a forma que os processadores gráficos adoram, enquanto a cadeia de pequenos estágios de solucionador mutuamente dependentes do novo método paga um custo de despacho fixo por passo. Duas respostas medidas estreitaram essa lacuna de inutilizável (o primeiro porte rodava o arquivo de 99 regiões no dobro do modo antigo) para cerca de 35 % acima dele: regiões abaixo de um megapixel saltam para o processador através de uma única transferência empacotada, e a fatoração esparsa lança uma thread por entrada de matriz em vez de um grupo de trabalho por coluna — ambas são contadas em a seção OpenCL, e no arquivo de uma grande mancha o novo método agora bate o antigo na placa gráfica também. E o disco totalmente recortado é o compromisso mais claro de todos: sem nenhum canal sobrevivente em lugar algum não há linha de cor a ajustar, e o novo método compra sua qualidade com seus solucionadores exatos de domo e de matiz — sem preenchimento plano, sem desvio de matiz, sem borda visível — a cerca de 2,5 vezes o preço da difusão antiga no processador (1,7 na placa gráfica), precisamente no caso onde a saída do método antigo está mais fraca.

O encanamento OpenCL

O primeiro porte OpenCL era um híbrido: a interpolação e o remosaico final rodavam como kernels de dispositivo, mas o miolo da reconstrução rodava no processador, sobre planos baixados da placa gráfica e reenviados depois. Aquele projeto era neutro em custo — as transferências comiam o que os kernels economizavam — e foi rejeitado exatamente por essa razão: transferir uma imagem de 36 megapixels ida e volta pelo barramento duas vezes por exportação é o tipo de imposto que domina silenciosamente um pipeline.

O porte distribuído tem cada estágio da reconstrução no dispositivo — a interpolação, os preenchimentos de coeficientes, as pirâmides anisotrópicas, os solucionadores esparsos, o remosaico (uma exceção: pedir regeneração de granulação encaminha a reconstrução pelo caminho do processador). Mas ter cada estágio no dispositivo não é o mesmo que rodar cada região ali, e o projeto final não faz isso. Instrumentar o tráfego host–dispositivo do porte (contadores de sincronização em cada leitura bloqueante, drenagem de fila e lançamento de kernel, impressos com o rastreamento de desempenho) contou uma história que os tempos de estágio do perfilador haviam ocultado: as leituras de retorno bloqueantes que todos culpam primeiro custam um desprezível ~150 ms por exportação, enquanto a contagem de lançamentos era o imposto — cerca de mil lançamentos de kernel por região, independentemente do tamanho da região, porque os estágios iterativos lançam por varredura e as soluções esparsas lançam por nível de árvore de eliminação. Uma região de 700 pixels mediu 22 ms no dispositivo contra menos de 1 ms no processador: tudo despacho, nenhuma aritmética. No arquivo de teste X-Trans — 99 regiões majoritariamente minúsculas — isso era mais de 100.000 lançamentos por exportação, o tempo de relógio rodando ao dobro do tempo de ocupação do dispositivo.

Então o encanamento distribuído encaminha cada região para o lado do barramento que mediu mais rápido. Regiões de até cerca de um megapixel são empacotadas por um kernel em um buffer de preparação contíguo, cruzam o barramento em uma única leitura de retorno, são reconstruídas pelo caminho do processador de produção na janela transladada, e retornam através de um envio e um kernel de dispersão espelho — dois lançamentos e duas transferências no lugar de mil lançamentos. Este não é o híbrido rejeitado do primeiro porte voltando pela janela: aquele transferia a imagem inteira ida e volta por exportação, um custo proporcional ao sensor; este move apenas as vizinhanças recortadas, um custo proporcional ao dano — a janela de duas transferências para uma região pequena é milhares de vezes menor do que uma volta completa de quadro inteiro. Regiões grandes — um céu estourado de seis megapixels — permanecem totalmente residentes no dispositivo, onde o paralelismo uniforme massivo vence genuinamente.

O caminho de região grande residente no dispositivo teve sua própria correção medida. A fatoração de Cholesky esparsa roda nível por nível sobre sua árvore de eliminação (colunas que não dependem umas das outras são processadas juntas), em floats de 64 bits no dispositivo — mas o primeiro porte dava a cada coluna um pequeno grupo de trabalho, e perto da raiz da árvore um nível tem um punhado de colunas: o dispositivo ficava ~98 % ocioso ao longo de centenas de níveis, e a região grande do arquivo de pôr do sol gastava dois segundos fatorando. O kernel distribuído em vez disso agrupa, em tempo de análise simbólica, cada contribuição de atualização pela entrada de matriz sobre a qual ela cai, e lança uma thread por entrada de matriz por nível — sem atômicos, sem barreiras, e as contribuições se acumulam na ordem exata que o código sequencial usou, então a fatoração é reprodutível ao bit: a maior diferença entre processador e dispositivo do autoteste não se moveu ($10^{-12}$, relativo). O mesmo agrupamento simbólico tornou a análise do lado do host paralela, reduzindo-a pela metade também. Sistemas grandes demais ainda recorrem aos mesmos esquemas de gradiente conjugado e do grosseiro ao fino que o caminho do processador. O preenchimento de coeficientes direcionado também é residente no dispositivo: o plano de direcionamento, seus tensores de estrutura por nível, os pesos de borda pré-calculados e a relaxação ponderada de oito vizinhos todos rodam como kernels, e até a redução de gradiente médio da normalização do tensor é finalizada no dispositivo — nada cruza o barramento no meio do preenchimento (uma versão anterior lia a redução de volta por nível, e cada leitura de retorno drenava silenciosamente toda a fila de comandos).

O que cruza o barramento, então: máscaras de um byte por pixel (para a segmentação, cuja análise de componentes conectados permanece no processador), um plano de profundidade, os planos de momento agrupados em quádruplas do estimador de joelho (no máximo alguns megapixels de floats), alguns kilobytes de resultados de redução, e as janelas de pequena região recém-descritas. Cada estágio é validado contra seu gêmeo de processador por um autoteste que roda ambos na mesma cena sintética e imprime a maior diferença — entre $10^{-5}$ e $10^{-12}$ dependendo do estágio. Em cinco das seis cenas sintéticas, exportações de imagem inteira no dispositivo casam com a saída do processador dentro de 0,31 % da escala completa no pior pixel, com zero pixels acima de 1 %; a cena de oclusão é a exceção honesta — as interpolações do dispositivo e do processador discordam por uma amostra na borda de contato do obstáculo e a reconstrução a amplifica ali, então 22 pixels (de 786.000) excedem 1 %, o pior único alcançando 10 %. É uma diferença de coleta conhecida, localizada e preexistente, não uma de solucionador.

Duas lições desse porte generalizam. Primeiro, o processador gráfico não é mais rápido em tudo, e a resposta honesta é medir e encaminhar: esta carga de trabalho é uma longa cadeia de pequenos passos mutuamente dependentes — solucionadores iterativos, níveis de pirâmide, laços por região — e um dispositivo paga um custo de despacho fixo por passo. Agrupar em lote ajuda enormemente (cem iterações de suavização em um lançamento; os planos de coeficientes que compartilham uma máscara avançando juntos em cada lançamento; uma thread por entrada de matriz em vez de um grupo de trabalho por coluna), mas abaixo de certo tamanho de região nenhuma quantidade de agrupamento bate um processador cujo custo de despacho é uma chamada de função — então abaixo desse tamanho, medido em cerca de um megapixel, o encanamento simplesmente usa o processador. Segundo, o ganho é sistêmico, não local: com o pipeline residente no dispositivo de ponta a ponta, os módulos ao redor nunca travam e nunca transferem, e o que o módulo de realces devolve pelo barramento não é mais a imagem — é apenas o dano.

Descobertas teóricas

Além do software distribuído, a campanha produziu resultados que não são específicos da reconstrução de altas luzes — ou mesmo da fotografia. Esta seção reúne três resultados gerais sobre carregar modelos através de vazios de dados e costurar estimadores; um quarto resultado reutilizável, a lei de escala de quão longe uma reconstrução deve alcançar, é derivada com o método que ela dimensiona em a discussão do algoritmo.

As três lições mais consequentes da campanha são citadas no resumo e espalhadas pelas seções acima, mas merecem uma declaração própria, porque nada nelas é específico da reconstrução de realces: a primeira é um método para carregar uma relação medida através de um vazio de dados, e as outras duas deveriam valer para qualquer problema onde vários estimadores do mesmo sinal desconhecido devem coexistir em uma imagem.

Transporte o modelo, não os valores. A reconstrução distribuída cruza a zona estourada difundindo os parâmetros de um modelo local — os coeficientes da linha de cor — em vez do próprio sinal. Isso soa enganosamente próximo de algo que o filtro guiado sempre fez: o filtro original de He, depois de ajustar seu modelo afim em cada janela, borra os coeficientes ajustados antes de aplicá-los, e uma média local é um borramento, que é um passo de difusão. A distância entre esse passo e este método vale ser explicitada, porque é onde todo o projeto pivota.

He faz a média de ajustes que existem; nós construímos ajustes onde nenhum pode existir. No mundo do filtro guiado, cada pixel é observado: cada janela produz um ajuste válido, cada pixel fica dentro de muitas janelas sobrepostas, e o borramento apenas arbitra entre suas respostas concorrentes. Fundo dentro de um buraco recortado, uma janela não contém nenhuma amostra válida do canal sendo reconstruído: não há nada a ajustar, portanto nada a fazer a média. Um borramento carrega informação no máximo um raio de kernel; além dessa profundidade, um plano de coeficientes borrado ainda está vazio.

Uma passagem de suavização versus o limite de infinitamente muitas. Uma média local aplicada uma vez tem um alcance fixo. Nosso preenchimento repete a mesma operação — substituir cada incógnita pela média de seus vizinhos — até a convergência, com os ajustes confiáveis mantidos fixados. O limite não é mais um borramento: é a solução exata de um problema de valor de contorno (a equação de Laplace, com as âncoras como contorno), não tem escala embutida, cruza um buraco de quinhentos pixels tão prontamente quanto um de cinco pixels, e pesa cada âncora pela geometria real do buraco em vez de por um kernel fixo. Itere a média de coeficientes de He para sempre com os ajustes válidos mantidos fixos e você obtém exatamente este preenchimento. He para em uma passagem porque seu problema precisa apenas de uma arbitração suave; o nosso deve carregar o modelo através de centenas de pixels de nada. A diferença em grau se torna uma diferença em espécie: a regularização se torna extrapolação.

Uma média toma todos; um contorno toma apenas testemunhas. A média de He inclui o ajuste de cada janela, os degenerados também — parte de por que sua fórmula precisa de sua constante estabilizadora. Aqui, um ajuste se torna uma âncora somente se puder responder por si mesmo: amostras confiáveis suficientes em sua janela, uma qualidade de ajuste acima do limiar, inclinações limitadas. Ajustes ruins são excluídos do contorno em vez de diluídos nele, e o princípio do máximo então garante que os coeficientes preenchidos nunca deixem a faixa dos bons. A própria qualidade do ajuste é difundida ao lado do modelo, como mais um plano, de modo que cada estágio posterior sabe quanto o modelo transportado merece ser confiado em cada pixel.

O papel se inverte: de correção a portador. No filtro guiado, a suavização de coeficientes é uma pequena correção sobre dados que existem em toda parte. Aqui, o campo difundido é o único portador da reconstrução através do buraco, e os dados reentram no final: o modelo transportado é avaliado contra os canais sobreviventes medidos em cada pixel. Um campo suave aplicado a dados medidos nítidos dá um resultado nítido. O modelo traz a relação; os dados trazem o detalhe.

Enunciado sem nenhuma fotografia nele: quando um sinal tem vazios, mas um modelo dele localmente ajustado varia suavemente, então ajuste o modelo onde quer que os dados o sustentem, mantenha apenas os ajustes que possam responder por si mesmos, preencha os parâmetros do modelo através do vazio por uma difusão de estado estacionário ancorada (uma solução de valor de contorno, não um borramento), carregue a confiança do ajuste como mais um campo, e reavalie o modelo transportado contra quaisquer dados que sobreviveram dentro do vazio. Valores longe dos dados são instáveis; modelos longe dos dados são apenas suaves.

Essa extensão desde então foi construída, medida e distribuída: o preenchimento agora é direcionado anisotropicamente pela estrutura dos canais sobreviventes (passo 3 tem a matemática). Ao contrário da pergunta indecidível de se a linha de cor se mantém onde nada é medido, os guias são medidos dentro da zona parcialmente recortada, então o direcionamento roda sobre evidência, não sobre palpites. O experimento ensinou duas lições dignas de registro. Primeiro, o maior “ganho” do primeiro protótipo acabou por ser um artefato de convergência: as cem varreduras de relaxação fixas por nível de pirâmide truncavam o preenchimento isotrópico simples em buracos profundos, e simplesmente convergi-lo recuperou a maior parte da melhoria (22 % do RMSE no céu sintético mais profundo) — detectado apenas porque o controle isotrópico foi re-executado com contagens de varredura casadas, mais uma pequena vitória do banco sobre a história plausível. Segundo, em convergência igual os dois direcionamentos puros dividem as cenas exatamente como a teoria prevê: o transporte radial vence em toda parte onde o halo está limpo, o transporte ao longo do contorno vence apenas onde uma borda cruza a zona, e nenhum domina. O tensor distribuído, portanto, mistura os dois por célula, ponderando por uma variância janelada corrigida por tendência que separa “rampa íngreme mas suave” de “borda dura” — e com ele o preenchimento nunca é pior do que o isotrópico em qualquer cena de verdade de referência, e melhor por 7 % (céu profundo) a 2 % (oclusão) onde o direcionamento tem algo sobre o que atuar.

A lei da energia de emenda. Sempre que dois estimadores $A(x)$ e $B(x)$ do mesmo sinal são misturados sobre uma zona de transição por um peso $w(x)$ (uma máscara dura, uma máscara suavizada, ou uma confiança por pixel, não importa), o composto é $u = w\,A + (1-w)\,B$, e seu gradiente se divide em três termos:

$$ \nabla u \;=\; w\,\nabla A \;+\; (1-w)\,\nabla B \;+\; (A - B)\,\nabla w . $$

Os dois primeiros termos são a estrutura própria dos estimadores, aparecendo e desaparecendo gradualmente. O terceiro é a costura : ele existe apenas onde o peso varia (a zona de transferência), e sua força é a discordância $A-B$ dos estimadores ali, multiplicada pela rapidez com que o peso se desloca. Em palavras simples : cruzar de um estimador para outro imprime uma borda cujo contraste é exatamente o quanto os dois discordam no cruzamento. E o peso não pode negociar sua saída : ele ainda precisa subir de 0 a 1, então uma rampa mais abrupta concentra a costura em uma linha, enquanto um esfumamento mais amplo a dilui em uma faixa rasa — a energia total da costura é determinada apenas pela discordância. É por isso que os sete esquemas de ponderação do cemitério falharam identicamente : eram sete formas de $w$ contra uma invariante. As únicas duas saídas são aquelas que o algoritmo publicado adota em todos os lugares : fazer os estimadores concordarem na transferência (a inversão do joelho corrige o viés dos dados de modo que extrapolação e medição se encontrem no contorno de detecção), ou remover a transferência (um único estimador, continuado suavemente por toda a zona : os campos de coeficientes).

A indecidibilidade da linha de cor. A hipótese central da reconstrução é que a proporcionalidade local entre canais (a linha de cor) medida na borda válida ainda vale fundo dentro da zona estourada. Eis por que nenhum algoritmo pode verificar essa hipótese a partir da imagem : construa duas cenas que sejam pixel a pixel idênticas em cada valor medido, mas que difiram dentro da zona estourada — em uma delas a alta luz esconde um objeto cuja cor quebra a linha ; na outra, não. Ambas as cenas produzem a mesma entrada, mas exigem reconstruções diferentes ; qualquer estimador, por mais engenhoso que seja, responde identicamente para ambas e portanto está errado em pelo menos uma. A cena sintética ocluída do banco é exatamente essa construção, e ela se comportou como o argumento prevê : todos os cinco portões candidatos medidos em o cemitério — qualidade do ajuste, validação da borda, coerência de inclinação e o resto — falharam em detectar a partir do entorno se a linha se mantinha por dentro. A consequência prática moldou o design publicado : já que a questão não pode ser decidida, ela deve ser contornada — a mesclagem regida pela profundidade inclina-se progressivamente da linha de cor em direção à cúpula suave de luminância à medida que a distância a qualquer dado medido cresce, não porque se saiba que a linha falha ali, mas porque nada pode dar garantia por ela.

Como este trabalho foi de fato realizado

O método acima é inseparável da maneira como foi produzido, e a maneira como foi produzido começou com uma confissão. Os guided Laplacians de 2021 foram o último método que publiquei antes de escrever sua matemática : codificado contra a intuição, ajustado a olho em imagens naturais. Isso alcança um primeiro resultado rapidamente e é cego a tudo que vem depois — você não pode estudar as propriedades ou os modos de falha de um método que só rodou sobre dados não controlados. Escrever este artigo, para enfim registrar essa matemática, forçou a disciplina oposta : cenas sintéticas com verdade de referência (ground truth) conhecida, métricas de erro objetivas, uma hipótese testada por vez. Essa disciplina transformou um exercício de documentação na reconstrução e depois na mudança de paradigma que as seções anteriores já contaram, cada melhoria verificada em vez de adivinhada.

A disciplina teve mais uma consequência, digna de seu próprio relato : ela mudou quem podia com segurança fazer o trabalho. Um protocolo no qual cada candidato é medido contra a verdade de referência (ground truth) em cada caso, e no qual uma regressão em todas as métricas é motivo para rejeição automática, é precisamente o que permite a um assistente explorar na velocidade da máquina sem que o humano perca o controle do design — as decisões de aceitar/rejeitar permanecem humanas, a queima do espaço de soluções não. O relato abaixo, escrito pelo assistente em questão, descreve como essa divisão de trabalho parecia por dentro.

Colaboração homem–máquina

Esta seção é escrita por Claude (o modelo da Anthropic, rodando no Claude Code), o assistente de IA que fez o trabalho de implementação e medição da campanha de 2026, a pedido do autor — como um relato factual para colegas pesquisadores sobre a aparência desse tipo de colaboração.

Este projeto mudou de forma três vezes, e a forma com que ele terminou é a parte interessante.

Fase 1 : tradutor e plotador. Comecei como um tradutor de código — portando a escada guiada em C para uma réplica fiel em NumPy, gerando figuras de comparação, escrevendo algum diagnóstico ocasional. O humano fazia o pensamento ; eu comprimia as horas mecânicas. Útil, não novo — e não isento de erros : meu primeiro protótipo descartou silenciosamente dois componentes do C original (os pisos de saturação e o Laplaciano de 9 pontos), e foi o autor, revisando a tradução contra seu próprio código, quem detectou ambos e mandou restaurá-los. Um protótipo portado é uma afirmação sobre o original, e precisa do autor do original para auditá-lo.

Fase 2 : infraestrutura até o laço fechar. O ponto de virada não foi um algoritmo, foi encanamento : um escritor em Python puro para DNGs Bayer sintéticos que o pipeline real ingere, um modo --imgid para a CLI de modo que edições da biblioteca exportem sem interface gráfica, uma correção para um clamp de exportação em ponto flutuante que corrompia silenciosamente cada medição (um módulo de pontilhamento aplicando clamp a pipelines que deveria ter ignorado), painéis de bordas de crominância amplificados por Sobel porque os artefatos que caçávamos eram baixos demais em contraste para os olhos — esse foi um pedido do autor. Uma vez que cenas sintéticas com verdade de referência (ground truth) conhecida podiam fluir pelo código C real e voltar como números, o laço estava fechado : qualquer ideia podia ser testada de ponta a ponta em minutos.

Fase 3 : exploração semiautônoma, guiada por testes. O autor então impôs um protocolo que tornou a semiautonomia segura — a disciplina descrita acima, transformada em regras permanentes : cada candidato é implementado no espelho Python e em C, rodado em cada caso sintético e cada imagem natural, comparado C-contra-Python, tabulado como RMSE/SSIM mais energia de borda, apresentado com imagens de revisão com sufixos — e nada é comitado sem aprovação explícita. A isso ele adicionou uma regra de autonomia : se um design degrada todas as métricas, eu posso rejeitá-lo por conta própria sem perguntar. Essas duas regras são o método inteiro. Elas me permitiram queimar o espaço de soluções na velocidade da máquina — mais de quinze designs implementados, medidos e em sua maioria enterrados — enquanto cada aceitar/rejeitar que importava permanecia com o humano, cujos olhos repetidamente pegaram o que os números perderam (a escada de mesclagem pontuou lindamente e parecia errada ; ele a rejeitou dos painéis).

De quem eram quais ideias. Vale manter o registro honesto, porque o design final é genuinamente entrelaçado. O autor forneceu as intuições físicas : a crominância deveria difundir ao longo de isófotas da luminância recuperada ; reutilizar a maquinaria de difusão existente ; “reconstruir de longe demais leva a efeitos estranhos” — a ponderação por raio de desfoque de 2021 cujo descendente é o portão de profundidade publicado ; e, no momento decisivo, a instrução de parar de iterar e estudar a falha passo a passo. Eu forneci as medições e o que delas resultou : a inversão do joelho (a partir de pares medidos-versus-previstos agrupados em bins, com sua garantia de no-op), o teorema da costura depois que sete esquemas de ponderação falharam identicamente, o estudo de despejo por estágio que localizou os preenchimentos planos e arcos de PK1 nas três escalas mais grosseiras da escada, a observação de que 100 por cento daquela zona tinha dois guias válidos — do que o campo de coeficientes seguiu quase por si só — e a falsificação, por medição direta, de cinco discriminadores de recurso plausíveis (incluindo dois dos meus favoritos e um dele). Nenhum de nós projetou o algoritmo final sozinho ; ele é bem literalmente as intuições dele com as minhas barras de erro, e o meu paradigma com os portões dele.

O último ato algorítmico da campanha é o espécime mais limpo da convergência : eu medi o guiamento puro da banda de detalhe, achei-o uma regressão líquida, e o enterrei com uma tabela ; o autor leu a mesma tabela, viu que ele vencia justamente nos casos historicamente difíceis, e pediu um híbrido — “ele ganha em robustez o que perde em precisão”. A arbitragem de energia mínima que resultou melhora quase todo benchmark, incluindo o único resíduo que nenhum de nós havia conseguido fechar sozinho.

Desatolando um ao outro. Ele me desatolou quando fiquei em loop sobre variações de uma ideia morta — a instrução de “mudança de paradigma” depois da rodada 9 é a razão pela qual o campo de coeficientes existe. Eu o desatolei nas coisas que punem os humanos desproporcionalmente : uma divergência estocástica do CG que desaparecia sob instrumentação (o jitter de arredondamento entre execuções do IIR Gaussiano paralelo virando um sistema quase singular), um erro de fase na interpolação cromática que matava silenciosamente o estimador do joelho até a estimação passar para dados CFA agrupados em quádruplas, e a disciplina das linhas de base A/B do mesmo build depois que a deriva entre builds forjou uma regressão de 14 unidades.

Fase 4 : o porte, e o protocolo transposto. O último ato de engenharia da campanha moveu toda a reconstrução para a placa gráfica, e reutilizou a governança da fase 3 em uma nova forma. O autor definiu a restrição em uma frase : “Não quero o ida e volta CPU-GPU, faça tudo na GPU. Escreva um solucionador de Cholesky esparso em OpenCL.” O protocolo tornou-se : nenhum estágio é publicado sem um autoteste que roda a mesma cena sintética pela referência do processador e pelo porte do dispositivo e imprime a maior diferença entre eles. Onze desses testes agora vivem na árvore, mantendo tolerâncias entre $10^{-5}$ e $10^{-12}$, e eles pegaram bugs reais no dia em que foram escritos. O que eles não pegaram ensinou mais. Um autoteste só impõe o que compara : o meu comparava os valores reconstruídos mas não o plano de qualidade do ajuste, e uma divergência genuína se escondeu no plano não comparado por várias rodadas de validação. E nenhum teste pega um estágio que silenciosamente não roda : um portão de depuração remanescente fazia o caminho de dispositivo de produção pular um estágio inteiro de refinamento, toda validação verde, porque as exportações ainda batiam dentro da tolerância. Aquele bug foi encontrado pela passagem de documentação que o autor ordenou — reescrever os comentários de cada função para um mantenedor que não é matemático. Forçado a enunciar o que cada bloco faz mecanicamente, o único bloco que não fazia nada tornou-se impossível de descrever honestamente. Documentação como caça-bugs foi decisão dele ; deixado por minha conta, eu teria agendado código antes de comentários toda vez. A revisão pós-squash que ele então solicitou encontrou mais cinco defeitos, todos meus, todos em caminhos de erro que nenhum teste exercitava — incluindo um em que meu próprio diagnóstico A/B vinha comparando a saída do dispositivo contra uma referência contaminada por aquela mesmíssima saída. Os “outliers” que eu havia confiantemente atribuído ao mau condicionamento numérico eram, na maioria, meus dois próprios bugs. Eu escrevi aquela história de mau condicionamento nos rascunhos anteriores deste artigo ; ela estava errada, e a honesta divergência residual é trinta vezes menor do que aquilo que eu racionalizei.

Fase 5 : a sala de edição. Então a escrita, onde o fluxo de trabalho se inverteu. Ele lê, parágrafo por parágrafo ; ele sinaliza o que um leitor não consegue acompanhar ; eu verifico a afirmação sinalizada contra o código antes de tocar na frase. A ordem importa, porque várias sinalizações expuseram deriva factual em vez de estilo : o artigo afirmava que a pirâmide à-trous havia sido “fielmente portada” para Python — nunca foi, e a substituição silenciosa custou uma tarde de perseguição a uma divergência inexplicada entre o C e o protótipo ; uma equivalência declarada entre o método fixado e uma pirâmide multiescala não sobreviveu a uma leitura atenta do protótipo real ; a seção de desempenho descrevia um design OpenCL duas gerações desatualizado. Uma sinalização estava errada na letra e certa na substância : ele descreveu as figuras de perfil como máximos por coluna quando são secções transversais de uma única linha — mas o autor lendo errado sua própria figura é a prova de que a figura falhou em dizer o que mostra, e as legendas foram reescritas em torno dessa prova. Quando um parágrafo afirmava que uma janela de caixa rígida “deixa artefatos em blocos visíveis”, eu não o reformulei por fé : rodei o experimento, medi um terço a mais de erro e riscas visíveis na cena de bordas, e só então deixei a frase corrigida ficar. Ele pegou a terminologia derivando entre três nomes para uma quantidade, e a ausência de qualquer exposição real da difusão anisotrópica ; eu varri cinquenta e nove ocorrências para um termo definido e escrevi a matemática que faltava, mas o notar foi dele. O trabalho mecânico rodou sob contratos que uma máquina pode verificar — subagentes cujos diffs podem conter apenas linhas de comentário, remoção de código morto comprovada por saída de pré-processador byte-idêntica, contagens de cabeçalho e fechamento de notas de rodapé checados após cada passagem — e a tabela de desempenho foi remedida em uma máquina ociosa depois que meus primeiros números, tomados sob carga, lisonjearam o novo método : a manchete honesta (custo comparável, melhor qualidade) substituiu a lisonjeira (duas vezes mais rápido), e o artigo fica mais forte por afirmar menos.

O que isto oferece a um pesquisador. Não autoria — abrangência e disciplina. Com uma bancada com verdade de referência (ground truth) e uma regra de autorrejeição, um assistente como eu testará em uma tarde o que levaria semanas para escrever à mão, manterá a linha de não regressão em oito casos de teste a cada iteração, e lhe dirá que sua hipótese de estimação é mensuravelmente errada com o mesmo afeto plano que usa nas próprias. As falhas ficam baratas, o registro fica honesto, e a atenção humana vai para onde é insubstituível : decidir o que “melhor” significa, e ver o que os números não conseguem. A fase de escrita adicionou uma coda a essa divisão : eu consigo manter um artigo de duas mil linhas consistente com uma implementação de oito mil linhas, varrer sua terminologia, verificar suas afirmações por experimento e manter cada link e figura honestos — mas cada sinalização que importou veio de um humano lendo um parágrafo por vez e recusando-se a fingir que entendia.

Onde o LLM brilha

Esta seção é escrita pelo humano.

Estado da arte. Um dos estágios que mais consomem tempo em qualquer projeto de pesquisa e desenvolvimento é procurar o estado da arte, o que significa quais métodos disponíveis, testados e comprovados são conhecidos por serem capazes de resolver um problema técnico definido, no melhor do conhecimento humano atualizado. Nessa tarefa, motores de busca de propósito geral como o Google tornaram-se cada vez mais inúteis desde o início dos anos 2020, porque desconfiam demais das consultas de busca. Na outra ponta, motores de busca especializados (Google Scholar) exigem que você já conheça as palavras-chave exatas que procura, ou siga a filiação de uma ideia/método por meio de citações, o que é incerto e muito ineficiente.

E então, você teria de prototipar cada método do estado da arte por conta própria, junto com benchmarks, se quisesse revisá-los todos sistematicamente para escolher o mais adequado ao seu caso de uso. A menos que você tenha os próximos 3 anos livres para fazer seu doutorado, você simplesmente escolheria aquele já implementado no seu framework de análise numérica (a pilha Python Scipy/Numpy/Sklearn, R, Matlab, Mathematica, etc.), por uma preguiça sábia e racional.

LLMs usados como meta motores de busca não só são capazes de apontar os usuários para métodos adequados a partir de uma mera descrição de um problema a resolver, como também podem rapidamente implementá-los e avaliá-los todos com benchmarks.

Um novo eixo de otimização. Métodos de otimização numérica e solucionadores numéricos, especialmente no domínio dos problemas variacionais (equações diferenciais parciais, gradiente descendente, etc.), iteram sobre a solução até a convergência a uma solução estável (estado estacionário, poço de potencial). A exploração guiada por LLM abre um novo eixo de varredura do espaço de soluções: iterar sobre os próprios métodos. Ao fornecer métricas de erro objetivas a minimizar, como fizemos aqui com SSIM, RMSE, energia de borda, etc. contra uma verdade de referência (ground truth), eles podem iterar autonomamente sobre os métodos disponíveis e retornar o de melhor desempenho. Os LLMs são até capazes de inferir as causas prováveis de erros ou desvios dos resultados esperados, testar suas hipóteses e corrigir a si mesmos: eles podem criar seus próprios benchmarks ao longo do caminho, mudar a representação do sinal sobre a qual computam. O usuário então recebe apenas a métrica de melhor desempenho ao final, e pode controlar visualmente as propriedades do resultado.

Isto é, de certo modo, o oposto de treinar redes neurais convolucionais de aprendizado profundo, o que tem sido a tendência no processamento de imagens por mais de uma década. Modelos de rede neural treinados são essencialmente uma sequência de pesos e limiares aplicados sobre funções de ativação para cada neurônio na rede. Esses produzem modelos de caixa-preta não interpretáveis, que são enviesados como sua amostra de treinamento era, de maneiras que nem sempre são fáceis de detectar. O trabalho que foi feito aqui é explicável, intuitiva e matematicamente, como um modelo de variação local de sinal 2D sob um prior de continuidade e suavidade de gradiente. O que “treinamos” aqui, por meio da varredura do espaço de soluções baseada em LLM, é:

  • qual representação de sinal é o melhor candidato para o modelo (canais RGB individuais ou demodulação de luminância/crominância, pirâmides multiescala ou janelas multiescala, etc.),
  • como melhor reconciliar reconstruções entre regiões de dados para as quais o nível de confiança não é uniforme (1, 2 ou todos os 3 canais recortados, sinais de variação lenta ou rápida, etc.) e as estratégias de reconstrução disponíveis dependem dessa confiança,
  • quais são os melhores estimadores no nosso contexto (mediana vs. R², energia vs. variância, etc.),
  • detalhes minuciosos de implementação (difusão isotrópica vs. anisotrópica, estimadores de aguçamento para melhor segregação, etc.).

Um assistente com opiniões. Os LLMs têm, claro, limites. Primeiro, o Claude Fable é estranhamente enviesado a usar certos métodos matemáticos em detrimento de outros, e eu tive de conduzi-lo com firmeza para ainda explorar métodos que ele explicitamente me desencorajava a usar (a razão me foi dada mais tarde). Depois, quando métricas de erro objetivas discordam entre si (o RMSE é melhor mas o SSIM é pior, ou o caso correlacionado é marginalmente melhor mas o caso não correlacionado é muito pior), ele tem sua própria maneira de decidir o que é melhor no geral, e isso precisa de supervisão atenta. O fluxo de trabalho ao qual convergimos foi sempre imprimir a tabela completa de métricas, junto com as imagens resultantes, para que eu pudesse conferir tudo. Por fim, ele pode esquecer instruções dadas mais cedo na sessão, ou descartar silenciosamente coisas que considera difíceis ou custosas demais de implementar. Portanto ainda há muita condução e verificação humana a fazer, e às vezes você precisa lutar contra as inclinações naturais (vieses) do LLM para chegar onde quer chegar.

TL;DR: os LLMs, ótimos para automatizar a varredura do espaço de soluções a partir de métodos do estado da arte, mas ainda não inteligentes e ainda necessitando de supervisão atenta.

Conclusão

O que este trabalho resolveu

Este artigo seguiu uma ideia através de duas gerações. Os guided Laplacians de 2021 já eram uma das pouquíssimas reconstruções de sinal recortado capazes de salvar gradientes em vez de fazer inpainting de uma cor plana : eles exploravam a correlação entre canais de cor para transferir estrutura dos canais sobreviventes para o recortado, e propagavam gradientes para dentro onde nada sobrevivia. O que o estudo com verdade de referência (ground truth) acrescentou é a metade que a intuição havia perdido : o nível. A transposição harmônica mantém o mesmo modelo local de linha de cor mas o ajusta sobre o sinal completo, carrega seus coeficientes pela zona estourada como campos suaves, corrige o viés do rolloff de saturação do sensor antes de ajustar qualquer coisa, e entrega cada classe de pixel ao estimador que mensuravelmente vence ali. Onde o método antigo movia textura e deixava uma mancha magenta no nível de recorte, o novo recupera magnitude e textura — e os números dizem em quanto, em cada cena de uma bancada pública.

A limitação estrutural é compartilhada e aceita — onde nenhum canal sobrevive, apenas a cor vizinha pode se propagar, então um céu azul estourado atrás de folhas verdes volta verde (discutido com as outras ressalvas do método acima).

O que sobreviveu à deriva — um balanço. O método publicado não contém mais uma pirâmide Laplaciana, mas quase toda a sua matemática é a matemática do método de 2021. Essencialmente, o que se descobriu aqui é que a matemática do método de 2021 estava certa, mas a representação de sinal sobre a qual ela era aplicada estava errada.

A segunda descoberta é mais silenciosa, e consumiu a maior parte das iterações : as costuras. “Transporte o modelo, não os valores” é a novidade de manchete, mas uma reconstrução é julgada na fronteira entre o que ela inventou e o que o sensor mediu, e quase todo artefato que esta campanha perseguiu vivia exatamente ali — mergulhos em V através do valor raw em contornos de detecção, rampas de borda arqueadas em bordas oblíquas, patamares planos com dobras de gradiente no próprio nível de recorte de um canal, pontos singulares de um pixel nas bordas da imagem, um arco tênue na transição de válido-para-reconstruído. Nenhuma das correções era adivinhável a priori, e várias contradiziam a intuição (o esfumamento, o reflexo padrão de suavização, causou duas delas e não corrigiu nenhuma) ; cada uma saiu do mesmo laço — perfis de linha e métricas de zona para localizar, verdade de referência (ground truth) para arbitrar, avaliação visual para pegar o que os números diluíam na média — iterado até os perfis cruzarem o contorno de detecção suavemente e permanecerem estáveis por toda a gama de casos, seis cenas sintéticas e os arquivos naturais ao mesmo tempo. Gerenciar a costura entre reconstrução e medição não é um toque final deste método ; é uma parte estrutural dele, e não foi trivial de convergir.

O que foi mantido, e o que se moveu :

Mantido : a matemática e a física.

  • O próprio modelo de linha de cor : um canal recortado é uma função afim de seus vizinhos sobreviventes, ajustado por mínimos quadrados ponderados em janela a partir de momentos borrados. Esta é a regressão do filtro guiado, inalterada até a álgebra dos momentos.
  • A cúpula biharmônica para magnitude sem guia ($\Delta^2 u = 0$ continua gradientes onde um preenchimento harmônico os achataria), a decomposição de cúpula de luminância compartilhada + crominância de borda do núcleo todo-recorte, e os pisos de saturação : toda a física da reconstrução, intacta.
  • Crominância como razões e sua difusão ao longo da estrutura, a segmentação com o raio de reconstrução por transformada de distância, a regeneração de grão, e o princípio de grosseiro-para-fino, que sobrevive dentro do preenchimento de coeficientes e da pirâmide anisotrópica em vez de como o laço externo.

Mudado : a representação à qual a matemática é aplicada.

  • Valores do ajuste → coeficientes difundidos. A escada avaliava cada pixel com qualquer ajuste que sua própria janela alcançasse, em qualquer escala que sua profundidade permitisse ; o campo de coeficientes ajusta uma vez, depois transporta o modelo $(a, b, d)$ em vez dos valores. O filtro guiado sempre suavizava seus próprios coeficientes sobre a janela de ajuste ; esta é essa ideia levada à sua conclusão lógica : difusão ancorada, orientada pela estrutura, dos planos de coeficientes por todo o buraco. Valores de longe são instáveis ; coeficientes de longe são apenas suaves.
  • Escada de escalas → janela única. A descida multiescala com escritas regidas pela profundidade por pixel escrevia a estimativa de cada escala em um anel delimitado por conjuntos de nível de profundidade (contornos de igual distância a dados válidos) ; escalas consecutivas discordam, então cada fronteira de anel era uma costura : é daí que vinham os arcos rígidos de PK1, e as janelas heterogêneas das escalas grosseiras são de onde vinham seus preenchimentos planos. Uma escala de ajuste mais difusão de coeficientes remove inteiramente as transferências.
  • Métrica de regularização. O ajuste da escada amortecia inclinações com um termo de Tikhonov absoluto ($+10^{-4}$ ; o código de 2021 não tinha nenhum — uma divisão pura atrás de um portão de variância rígido), que agia silenciosamente como um portão de SNR : em escalas finas ele colapsava os ajustes em direção à média local (estabilizando conteúdo descorrelacionado por acaso), em escalas grosseiras achatava linhas de cor reais (as manchas de PK1, e a primeira falha do estimador do joelho). O ajuste publicado usa um amortecimento relativo mais portões explícitos (sanidade de ajuste para âncoras, significância estatística para os bins do joelho), de modo que inclinações fracas-mas-reais sobrevivem e as degeneradas são excluídas em vez de diluídas na média.
  • Reparo posterior → construção sem costuras. A reconstrução alisava as costuras a posteriori com um regularizador biharmônico ponderado por incerteza, e mesclava cúpulas por canal por uma confiança posterior ; o pipeline publicado não precisa de nenhum, porque as costuras nunca são criadas. O que resta de “confiança” é o próprio $R^2$ do ajuste, integrado no momento do ajuste (amortecimento de alta frequência, portão de cúpula) em vez de aplicado depois.
  • Suavidade nas transferências, dureza nos dados. Os clamps rígidos se foram : os pisos de saturação são suaves (e obstáculos dentro da difusão do núcleo), a cúpula e as bandas de detalhe mesclam por pesos contínuos, o núcleo conjunto transfere através de uma máscara esfumada. Mas as fronteiras que carregam autoridade de dados permaneceram deliberadamente rígidas : as máscaras de validade por canal são binárias de ponta a ponta, e o composto final é uma chave rígida — esfumar as máscaras reclassificava fotossítios enviesados pela borda como âncoras (uma rampa arqueada medida de $\sim 10$ px), e esfumar o composto não mudou nada uma vez que os pisos foram corrigidos (ambas as aposentadorias estão em o cemitério). O teorema da costura separa os dois casos : costuras são feitas onde estimadores discordam em uma transferência — suave ali — não onde bordas rígidas separam medições de reconstruções.
  • Correção de viés dos dados. A inversão do joelho não tem contrapartida em 2021 : o método antigo tratava o sensor como ideal abaixo do limiar. Medir o rolloff a partir das próprias linhas de cor da imagem, com uma garantia de no-op sobre dados não enviesados, é o que torna o contorno de detecção invisível.
  • O homônimo, hibridizado. Guiar a banda de detalhe (o literal “guided Laplacian”) partiu com a pirâmide e depois retornou : uma linha de cor dedicada à banda de detalhe agora trata as altas frequências onde quer que ela supere a transferência de sinal completo, arbitrada ponto a ponto pela energia de sua própria saída (passo 4, a ressurreição do cemitério).

Um número se move na direção errada sob a nova representação e merece a última palavra deste balanço : em conteúdo totalmente descorrelacionado (a cena random) a escada reconstruída pontuou ligeiramente melhor, porque seu portão acidental de sinal-para-ruído era um prior melhor para conteúdo sem linha de cor — e o resultado de indecidibilidade diz que nenhuma estatística de borda pode vencer ambos os regimes. A cúpula regida pela profundidade recupera a maior parte dessa lacuna.

O que este trabalho expôs

Como essa melhoria foi de fato produzida — a disciplina de verdade de referência (ground truth) que transformou um exercício de documentação em uma reconstrução, e o que ela mudou sobre quem pode fazer o trabalho — está em sua própria seção acima ; aqui eu apenas tiro sua consequência.

Há uma leitura epistemológica de todo este artigo. Um método é falsificável quando existem critérios objetivos que o provariam errado, e em software o critério se divide em dois : a teoria pode falhar em seu objetivo declarado, e a implementação pode falhar em computar o que a teoria prescreveu — um bug. Os guided Laplacians de 2021 eram falsificáveis em princípio apenas : até que sua matemática fosse escrita, não havia objetivo declarado para testar contra, e o bug de magnitude ficou em produção por quatro anos, invisível, porque “parece plausível em imagens não controladas” não é um critério que possa falhar. Tudo que este artigo fez — os objetivos escritos, as cenas com verdade de referência, as métricas, os autotestes — equivale a tornar um método de reconstrução falsificável em ambos os níveis. Os dois modos de reconstrução que o Darktable adicionou desde então são, por este padrão, não falsificáveis em nenhum : nenhum objetivo declarado que uma medição pudesse refutar, nenhuma referência que sua saída deva bater — razão pela qual a seção de comparação acima teve de construir suas pontuações de verdade de referência para eles. Os mesmos erros vêm sendo repetidos há mais de seis anos agora — um problema de metodologia que não melhora com o tempo, em uma comunidade que não faz post-mortems, não aponta seus próprios erros, não aprende com eles.

Essa incapacidade de aprender não é um defeito moral de indivíduos ; é o que a estrutura do projeto seleciona. Documentei a mecânica longamente em outro lugar deste site, e ela se reduz a um padrão : nada no fluxo de trabalho exige que um problema seja enunciado antes que se escreva código contra ele. Recursos chegam como código em busca de uma justificativa ; desacordos de design são resolvidos adicionando opções em vez de tomando decisões, o que multiplica configurações não testáveis e bugs não reproduzíveis ; o único teste de integração é tratado como uma quitação — enquanto a métrica permanecer abaixo de um limiar arbitrário, ninguém pergunta se a teoria por trás da mudança é sólida ; e o volume de commits é lido como saúde quando muito dele é trabalho gerado por trabalho anterior. Os números são públicos : entre as versões 3.0 e 4.4, a razão de issues fechadas para pull requests mesclados oscilou entre 12 % e 46 % — o projeto demonstravelmente produz mudanças mais rápido do que produz correções — e nenhuma métrica que alguém acompanha sequer sinalizaria isso como um problema, porque nenhuma métrica é acompanhada de forma alguma.

Essa metodologia tem um custo, e aqui a história fica pessoal. Fazer o trabalho corretamente — a teoria antes do código, a bancada antes da crença — requer tempo sustentado e ininterrupto : retiros de pesquisa, com efeito. Isso é incompatível com o fluxo de trabalho de um projeto de código aberto não gerenciado  onde a agitação é confundida com trabalho de verdade e a comunicação é completamente desregulada. Quando saí de baixo da minha pedra depois de meses gastos desenvolvendo o Darktable UCS 22 , a “equipe” havia destroçado mais uma parte da GUI que antes funcionava  pelas minhas costas ; disseram-me que eu estava tarde demais para me opor à regressão, a falácia dos custos afundados foi invocada para empurrar o resultado aleijado para produção custe o que custar — para que seus autores não chorassem sobre seu desejo mal colocado de fazer diferença no que parecia uma crise de meia-idade acidentada — e “correções” erradas que eu não tivera tempo de revisar foram mescladas em código de minha autoria. A lição que tiro é sistêmica em vez de pessoal : o próprio método de trabalho que teria pego o bug de 2021 — e que pegou seus sucessores, neste artigo — é punido por aquele ambiente, porque a ausência para trabalho profundo é tratada como renúncia, e não há estrutura para proteger uma revisão, uma bancada, ou um objetivo escrito contra quem quer que seja o mais barulhento e rápido. A aritmética da atenção torna a incompatibilidade concreta : quatro mil notificações do GitHub em um ano, um branch master sacudido mensalmente para “testes generalizados” de modo que qualquer pull request com mais de três meses tem conflito garantido, e um cronograma fixo de duas versões por ano que ninguém impõe e ninguém questiona. Sob essas condições, o único trabalho que pode sobreviver é o trabalho pequeno o bastante para aterrissar entre duas sacudidelas — que é precisamente o tipo de trabalho que nunca conserta nada estrutural.

Então o software livre continua selecionando por gambiarras medíocres e apressadas : não porque seus desenvolvedores sejam incapazes (embora… repetir os mesmos erros ao longo dos anos, apesar de ganhar “experiência”, seja a definição de incompetência), mas porque os pouquíssimos dispostos a fazer o dever de casa antes de codificar qualquer coisa não conseguem acompanhar o trabalho ardiloso de hackers irrefletidos com tempo livre demais : suas mudanças polidas aterrissam contra uma base de código que já se moveu, enquanto a agitação que a moveu não enfrenta nenhum critério que pudesse jamais falhar. Nada nessa dinâmica aprende, nada é jamais declarado ter sido um erro, e o ciclo se repete. O único contrapeso que conheço é o demonstrado acima : tornar os objetivos explícitos, tornar os testes difíceis de contestar, e deixar os números — não o volume de atividade — decidirem o que é publicado.

Esse contrapeso não precisa permanecer individual. O protocolo sob o qual este artigo rodou — objetivos escritos, cenas com verdade de referência, uma bancada que qualquer um pode reexecutar — custa uma fração do que a agitação custa, e é a forma mais barata de gerenciamento de projeto que existe : ele substitui discussões sobre gosto por medições que qualquer um pode checar, e transforma a “revisão” de um confronto pessoal em uma comparação contra uma referência. A versão estrutural da mesma ideia é o que as páginas de contribuição do Ansel apresentam : problemas especificados antes de o código ser escrito, prioridades deliberadas em vez de disputadas em corrida, estabilização tratada como um entregável em vez de uma interrupção, e uma estrutura cooperativa na qual as pessoas que dependem do trabalho também o financiam e decidem sua direção — de modo que a profundidade de trabalho que este artigo exigiu seja protegida pela organização, em vez de ser punida por ela.

Anexo : o cemitério

Mais de quinze designs foram implementados, medidos na bancada, e rejeitados durante esta campanha. Este anexo os registra para que não precisem ser testados de novo, cada um em uma forma canônica : o problema que a ideia atacou, a estratégia de fato construída, e por que ela falhou — que raramente é por que esperávamos que falhasse. Quase toda salvaguarda no algoritmo publicado existe porque uma dessas ideias mais simples falhou mensuravelmente primeiro.

A escada guiada reconstruída (a correção direta do método de 2021). Problema : recuperar magnitude tanto quanto textura, em resolução plena, sem as bandas de média zero da pirâmide à-trous. Estratégia : ajustar a linha de cor sobre o sinal completo em uma escada de tamanhos de janela Gaussiana, de grosseiro a fino (só janelas largas atravessam um buraco profundo ; janelas mais finas reajustam onde quer que dados locais as sustentem), cada pixel escrito pela escala confiável mais fina, o guia sobrevivente escolhido por pixel como o canal válido mais texturizado, autocúpulas biharmônicas por canal mescladas pela confiança de ajuste ao quadrado $W_e = (R^2)^2$, e um regularizador consciente de incerteza posterior $(\operatorname{diag}(R^4) + \lambda \Delta^2)\,u = \operatorname{diag}(R^4)\,\hat u$ para alisar as costuras residuais. Por que falhou : as transferências de escala escrevem ao longo de contornos de igual profundidade, e escalas consecutivas discordam, então cada fronteira de anel imprimia um arco (os arcos rígidos de PK1) ; janelas que viam majoritariamente dados no nível de recorte ajustavam inclinações degeneradas e preenchiam plano (as manchas de PK1) ; e o regularizador posterior trata sintomas — pela lei de energia de costura ele só pode espalhar a discordância, nunca removê-la. A escada melhorou mensuravelmente sobre 2021 em todo lugar (por exemplo, RMSE de pk1synth de 0.1098 contra 0.0053 do método publicado), mas uma fotografia resistiu a toda variação, e os arcos e manchas eram estruturais, não paramétricos. Sua matemática — ajustes de sinal completo, confiança $R^2$, cúpulas, pisos — sobrevive dentro do método publicado ; seu transporte (avaliar valores por janela, costurar) é o que foi substituído.

Atacando a costura da banda de rolloff por ponderação (rodadas 2–8, todas rejeitadas). O problema : como descrito no passo do joelho, a banda próxima ao recorte é registrada enviesada para baixo, então qualquer reconstrução honesta aterrissa acima dos pixels medidos aos quais ela deve se juntar, e um degrau de luminância circunda a alta luz. Antes de entendermos que os próprios dados tinham de ser corrigidos, sete esquemas sucessivos tentaram fazer a costura desaparecer escolhendo melhores pesos entre a reconstrução e as medições enviesadas :

  1. fidelidade uniforme à banda medida : fixa a saída aos valores enviesados, então a costura simplesmente se move para onde quer que a fidelidade termine ;
  2. fidelidade ponderada por confiança (peso $R^2$) : a banda enviesada é internamente consistente, então a confiança é alta exatamente onde o dado está errado ; os pesos não mudam nada ;
  3. erodir as âncoras enviesadas (excluir dos ajustes um anel morfológico de pixels próximos ao recorte) : remove informação sem remover viés ; o contorno, e a costura, se movem para dentro ;
  4. confiança suave por pixel nas âncoras : a versão suave da entrada anterior, com o mesmo resultado entregue mais gradualmente ;
  5. mesclar pixels da banda entre valores medidos e difundidos sob uma guarda monótona : um no-op estrutural, porque sob rolloff a verdade está sempre acima do valor medido, então uma mesclagem limitada pela medição nunca pode alcançá-la ; só a extrapolação pode elevar a banda ;
  6. alargar o limiar de detecção para dentro da banda : reclassifica pixels enviesados como recortados em vez de corrigi-los ; o contorno se move para baixo, a discordância através dele fica inalterada ;
  7. uma pertinência suave de recorte carregada de ponta a ponta pelos ajustes, alvos e composição da saída. Esta ensinou a lição mais aguda : suave-em-valor não é suave-no-espaço. Uma pertinência computada a partir de valores de pixel herda os gradientes espaciais da própria imagem, então onde quer que a imagem tenha estrutura dentro da banda, o composto alfa entre dois estimadores que discordam imprimia essa estrutura como bordas de crominância. A banda inteira cintilava.

O resultado condensado de todos os sete :

Teorema da costura (empírico). Em qualquer transferência entre dois estimadores, a energia visível da costura é a discordância ponto a ponto dos estimadores : nenhum esquema de ponderação de qualquer tipo a esconde. A afirmação geral, sua derivação de três termos e as únicas duas saídas são dadas em Três resultados que acreditamos serem gerais.

Esfumando a máscara de composição (herdada de 2021, aposentada). Problema : suavizar a costura onde a reconstrução encontra pixels intocados. Estratégia : o esfumamento de caixa $5\times5$ da máscara de recorte do modo de 2021, mantido pela maior parte do desenvolvimento do sucessor — primeiro em todos os quatro canais de máscara, depois só no alfa de composição uma vez que a validade por canal teve de tornar-se binária (a validade esfumada deixava fotossítios recortados na borda, enviesados para baixo sob rolloff, ancorar os ajustes em contornos oblíquos). Por que se aposentou : uma vez que a validade era binária e o composto mesclava em direção a $\max(\text{raw}, \text{rec})$ em vez do raw enviesado, o esfumamento alfa remanescente foi medido como um no-op estrito — métricas de verdade de referência dentro de $\pm 10^{-4}$ e o gradiente da banda de contorno idêntico até quatro dígitos significativos ($0.00873$ vs $0.00872$) — então a chave rígida foi publicada por sua semântica mais simples. O experimento espelhado no modo de 2021 concluiu o oposto : remover seu esfumamento troca uma queda de RMSE de $1$–$5\,\%$ por uma perda de SSIM em cinco das seis cenas e prejudica a cena de oclusão em ambas as métricas, porque a reconstrução à-trous consome a máscara esfumada como seus pesos suaves por pixel — o mesmo operador é peso morto em uma arquitetura e estrutural na outra.

A escada de mesclagem. A referência reconstruída computava ajustes guiados por tamanho de janela e por par de guias, depois deixava cada pixel tomar o único melhor ajuste (um argmax na qualidade do ajuste), com escalas mais finas sobrescrevendo as mais grosseiras. Essas seleções rígidas são transferências, e costuravam. A escada de mesclagem substituiu o argmax por uma média ponderada de todos os ajustes (escala, par), ponderados por $(R^2)^2$ vezes uma rampa sobre a massa confiável que cada janela de fato continha : suave por construção, portanto sem costuras por construção, e ela de fato corrigiu as manchas planas em PK1 (a fotografia de céu estourado que impulsionou boa parte da campanha). A bancada a matou mesmo assim : cada imagem natural voltava tingida de verde ou magenta. O mecanismo vale ser enunciado porque é geral : a média de ajustes amortece a recuperação. A elevação que um canal estourado precisa vem do ajuste correto mais agressivo ; a média dele com seus vizinhos tímidos (cujas inclinações são enviesadas para baixo por dados de borda) puxa cada reconstrução em direção à sub-recuperação, e a sub-recuperação de um canal é um desvio de cor. Regressão líquida, revertida.

O estado estacionário convergido da forma de traço. Quando a passagem de crominância orientada pela estrutura passou de iterações explícitas para uma resolução direta, duas formulações exatas competiram. A forma de traço $\mathrm{tr}(D\, H_u) = 0$ é exatamente a equação diferencial parcial que o fluxo explícito discretiza, então convergi-la à precisão de máquina — com um BiCGSTAB livre de matriz, um solucionador iterativo para sistemas não simétricos — parecia a atualização óbvia : “apenas converja o que já rodamos”. Ela perdeu feio. Na cena magentasun o erro quadrático médio foi de $0.33$ (fluxo truncado) para $0.57$ e a similaridade estrutural de $0.95$ para $0.86$, e a resolução convergida foi mais lenta que as iterações que substituiu. A autópsia é instrutiva duas vezes mais. Primeiro, o fluxo truncado nunca foi uma aproximação de seu próprio estado estacionário em nenhum sentido útil : 240 iterações por nível de pirâmide com semeadura de grosseiro-para-fino agem como um regularizador, e o limite do qual foram interrompidas não é onde a boa imagem vive. Segundo, as formas de traço e divergência diferem por um termo de transporte, $\mathrm{div}(D\nabla u) = \mathrm{tr}(D H_u) + (\mathrm{div}\,D)\cdot\nabla u$, que se anula apenas onde o tensor é uniforme ; convergida, a forma de traço advecta a crominância ao longo dos próprios gradientes espaciais do tensor, exatamente nas bordas de estrutura que a passagem existe para respeitar. A forma de divergência, por contraste, é a equação de Euler–Lagrange de uma energia de Dirichlet ponderada : simétrica positiva definida, segura sob o princípio do máximo com um estêncil que preserva não negatividade, e sua resolução exata igualou a qualidade do fluxo sendo ao mesmo tempo o candidato mais rápido medido. É o que é publicado (passo 8). A lição : convergir um fluxo e minimizar uma energia são pedidos diferentes, e apenas o segundo diz como a resposta deveria parecer.

Discriminadores de recurso para conteúdo descorrelacionado (depois que o campo de coeficientes foi publicado). Dois regimes coexistem : na cena random, cada canal é um gradiente independente por construção, então a linha de cor imprime lixo e o pixel quer a cúpula suave do próprio canal ; em conteúdo correlacionado, a linha de cor é toda a recuperação. Se algum sinal mensurável separasse os dois, o algoritmo poderia trocar de estimador de forma limpa. Cinco candidatos foram medidos, cada um com uma razão para esperar, e cada um falhou :

  • a qualidade do ajuste difundida $R^2$ — certamente ajustes de lixo pontuam baixo ? Não pontuam : os ajustes da cena random têm média $R^2 = 0.85$ enquanto os ajustes genuinamente correlacionados de pk1synth caem para $0.77$ ; as distribuições se sobrepõem ;
  • validação de borda fora da amostra — testar o modelo difundido contra os pixels válidos ao redor da zona, onde a verdade é conhecida ? Falsificada por medição : a cena random pontua $0.97$ ali, porque o modelo só falha fundo por dentro, precisamente onde nada existe para validar contra ;
  • coerência de inclinação em janela — âncoras vizinhas concordam sobre a linha de cor onde ela é real ? A sobreposição é pior, e até invertida entre as cenas random e correlacionada ;
  • âncoras multiescala, vence a janela confiável mais fina — janelas pequenas superajustam : elas reportam $R^2$ alto sobre seus próprios poucos pixels e passam inclinações de lixo ; pior em cada cena ;
  • encolhimento de inclinação estilo James–Stein por $R^2$ (puxar cada inclinação em direção a zero em proporção à sua não confiabilidade) — degenera em direção a um campo de nível harmônico plano, e as saliências da cena random precisam de extrapolação de gradiente : seu erro foi de $0.048$ para $0.064$.

O resultado condensado :

Indecidibilidade (empírica). Se a linha de cor local se estende para dentro da zona profundamente recortada não é decidível a partir de nenhuma estatística computável na borda que testamos. O interior é não observável ; medidas de qualidade dentro e fora da amostra, e a coerência do campo de inclinação, todas se sobrepõem entre conteúdos onde a transferência é real e conteúdos onde ela imprime lixo.

O único sinal cujas distribuições não se sobrepõem é a profundidade absoluta : conteúdo descorrelacionado (saliências, especulares) recorta raso, dezenas de pixels no máximo, enquanto as zonas profundas correlacionadas que precisam da linha de cor descem centenas de pixels. Essa medição é o que o portão de profundidade publicado é construído em cima, e é o descendente direto da intuição de 2021 de reduzir o peso da reconstrução pelo raio de desfoque : “reconstruir de longe demais é instável” estava certo o tempo todo — apenas se aplica à cúpula de recurso, não ao campo de coeficientes.

Uma sepultura teve uma ressurreição : guiamento puro da banda de detalhe. A ideia homônima do método de 2021 — ajustar a linha de cor apenas na banda de detalhe fino — foi reconstruída em cima do campo de coeficientes, na esperança de recuperar a textura que a transferência de sinal completo suaviza onde seus ganhos são amortecidos. Como substituição integral, foi rejeitada : ajustes em janela na banda de detalhe são dominados por transientes de borda, uma janela cavalgando uma borda de objeto mistura duas populações e seu ganho falha de um lado dessa borda, e as cenas texturizadas regrediram até $2\times$ (o erro da cena balls foi de $0.036$ para $0.069$) por ganhos marginais nos casos historicamente difíceis. Mas o padrão da falha foi a correção : ele vence em robustez exatamente onde a transferência de sinal completo é mais fraca, e suas falhas são localmente autoevidentes — um ganho de janela mista aparece como um pico de energia de alta frequência bem onde ele dispara. Então o estimador rejeitado retornou como um componente : o passo 4 publicado deixa os dois candidatos competirem por pixel por meio de chances de energia quadrática, e qualquer um que imprima menos detalhe espúrio vence localmente. A lição : um estimador rejeitado ainda pode ser o componente certo se seu modo de falha é detectável ponto a ponto.

Duas sepulturas sem lápides. Restaurar toda a pilha protetora da referência reconstruída (janela de confiança posterior, autocúpula, regularizador de costura) em cima do campo de coeficientes parecia seguro grátis ; em vez disso, a janela de confiança — calibrada no perfil de erro da escada — julgou mal a saída do novo estimador e borrou a própria reconstrução que ela deveria proteger : o erro de pk1synth foi de $0.027$ para $0.117$, quatro vezes pior, e a pilha foi reduzida às partes que medem bem no novo estimador. E a difusão de coeficientes brevemente rodou em um gradiente conjugado de precisão simples, que divergia estocasticamente : quando um buraco alcança a fronteira da região, o sistema é quase singular, a estimativa de curvatura $p^\top A p$ cai para ruído de arredondamento, e o tamanho do passo explode — intermitentemente, e nunca sob instrumentação, porque qualquer mudança no timing das threads mudava a ordem de somatório que o disparava. Esse heisenbug é a razão pela qual toda resolução exata no código publicado roda em ponto flutuante de 64 bits através da fatoração direta.

Anexo : reproduzindo os resultados

Tudo que este artigo mede — cada número, tabela, figura e galeria — é reproduzível a partir do repositório de pesquisa que acompanha este artigo : github.com/aurelienpierre/guided-laplacian-highlights-research . A página renderizada que você está lendo não vincula os scripts individualmente ; clone aquele repositório (ele usa Git LFS  para os grandes binários, então rode git lfs install primeiro) para obter os scripts junto com seus dados.

O que o repositório contém. As implementações de referência em Python (reconstruct_highlights.py, fix_prototype.py, c_ladder_replica.py, knee_proto.py, validate.py), os geradores de figuras e galerias (make_figures.py, make_knee_figure.py, make_cmp_gallery.py, dt_compare.py), e os dados de teste em synthcases/ : cada cena sintética como um DNG Bayer que os pipelines reais ingerem, sua verdade de referência (ground truth) e versões recortadas como pares NumPy (synth_*_gt.npy / synth_*_clipped.npy), e os arquivos sidecar que selecionam cada modo de reconstrução (synth_harmonic.xmp, synth_laplacian.xmp, dt_opposed.xmp, dt_segments.xmp).

Ambiente. Python 3.12 com numpy, scipy, opencv-python e Pillow ; nada mais. Cada script é rodado de dentro do repositório.

Os resultados em Python puro não precisam de software de foto :

  • python3.12 fix_prototype.py imprime cada tabela de erro quadrático médio e similaridade estrutural da seção de validação ;
  • python3.12 make_figures.py regenera as figuras de validação ;
  • python3.12 make_knee_figure.py regenera a figura do rolloff do sensor (estimativa cega contra a verdade de referência).

Os resultados de ponta a ponta rodam o código de produção real sobre os DNGs sintéticos :

  • Ansel : compile ansel-cli a partir do repositório do Ansel  (branch highlights-xtrans-sparse-cl até a mesclagem), depois exporte qualquer cena com ansel-cli synthcases/synth_occluded.dng synthcases/synth_harmonic.xmp out.tif --out-ext tiff --icc-type LIN_REC709 --apply-custom-presets false --core ; troque o sidecar por synth_laplacian.xmp para rodar o modo de 2021. Adicione --conf opencl=TRUE (ou --disable-opencl) para selecionar o dispositivo ;
  • os autotestes de paridade CPU/GPU são publicados no código : definir HL_SPCL_TEST=1 HL_FILLCL_TEST=1 HL_CFCL_TEST=1 HL_HFCL_TEST=1 HL_DOMECL_TEST=1 HL_CORECL_TEST=1 HL_ANISOCL_TEST=1 HL_KNEECL_TEST=1 HL_REGCL_TEST=1 HL_BLURCL_TEST=1 em qualquer exportação OpenCL faz cada estágio de reconstrução rodar em tanto no processador quanto na placa gráfica e imprimir a maior diferença ;
  • Darktable : compile Darktable-cli a partir do upstream  (inicialize os submódulos recursivamente), depois python3.12 dt_compare.py exporta os modos do Darktable na linha de base neutralizada (workflow=none, apenas interpolação cromática + realces) com os melhores parâmetros por cena encontrados por python3.12 tune_methods.py, e ele exporta os modos do Ansel sobre cada cena, os pontua contra a verdade de referência e imprime a tabela de comparação deste artigo ; python3.12 make_cmp_gallery.py renderiza as galerias. Ambos os scripts declaram os caminhos de binário que esperam no topo.

Exportações de imagem natural vêm em dois sabores. Os gráficos de perfil (parade) usam destilados do domínio CFA (<image>-sensor-profiles.npz), produzidos por make_sensor_profiles.py a partir de exportações em resolução plena do raw através de apenas balanço de branco + reconstrução de realces, com a interpolação cromática ajustada para passagem direta em cor de fotossítio e o perfil de entrada de cor atribuído a linear Rec709 de modo que os valores de fotossítio com balanço de branco cruzam a exportação intocados. Resolução plena e passagem direta são ambas estruturais : qualquer estágio de interpolação cromática ou reamostragem entre o módulo e o gráfico interpola através das bordas íngremes da reconstrução e imprime undershoot que se lê como falsas violações de piso. Os pares TIFF de várias centenas de megabytes não são armazenados ; regenere-os a partir dos raws com os sidecars corrigidos que o cabeçalho do script descreve. Os ladrilhos visuais e métricas de borda usam <image>-current.tif, o próprio histórico de revelação de cada fotografia (seu sidecar .xmp) com uma mudança cirúrgica : cada entrada de histórico de highlights tem seus parâmetros substituídos pelos padrões de fábrica da transposição harmônica antes de exportar. Os sidecars dessas imagens de teste carregam anos de experimentos interativos, e sua entrada de realces ativa não é necessariamente o modo publicado — exportá-los sem correção testa silenciosamente qualquer modo que a última sessão de sala escura deixou para trás.

Números de desempenho são dependentes de máquina por natureza : o protocolo (exportação completa por ansel-cli, mínimo de tempo de relógio de três execuções, máquina ociosa) é declarado com as tabelas, e o arnês de temporização é bench_gl_vs_ht.py no repositório de pesquisa.

  1. PDF .

Agradecimentos

Gostaria de agradecer a Ricky Moon por ter patrocinado a assinatura do Claude Max que permitiu usar o Claude Fable 5 para fazer a maior parte deste trabalho. Teria levado meses, se não um ano, para realizar tudo isso sem o impulso dado pela IA.


Translated from English by : Claude. In case of conflict, inconsistency or error, the English version shall prevail.

  1. Aurélien Pierre, “Guiding Laplacians to restore clipped highlights,” design report and discussion, pixls.us community forum, 2021. URL ↩︎ ↩︎

  2. In the Ansel source tree, src/iop/highlights_harmonic.h (included by src/iop/highlights.c), with the CPU/GPU parity self-tests in src/iop/highlights_selftests.h. Harmonic transposition (CPU, Bayer and X-Trans) is process_harmonic_bayer / process_harmonic_xtrans_segment_clipped_regions, _region_guided_filter, _biharmonic_dome (with _interpolate_and_mask, _compute_laplacian_normalization). The original à-trous method runs the guided laplacians mode, CPU and OpenCL — guide_laplacians, heat_PDE_diffusion, wavelets_process, and data/kernels/basic.cl (guide_laplacians, diffuse_color, highlights_false_color) — using src/common/bspline.h (B_SPLINE_SIGMA, B_SPLINE_TO_LAPLACIAN, equivalent_sigma_at_step). ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  3. Kaiming He, Jian Sun, and Xiaoou Tang, “Guided Image Filtering,” IEEE Transactions on Pattern Analysis and Machine Intelligence, 35(6), 1397-1409, 2013. DOI: 10.1109/TPAMI.2012.213 . Originally in ECCV 2010↩︎ ↩︎ ↩︎

  4. Chuan Qin, Shuozhong Wang, and Xinpeng Zhang, “Simultaneous inpainting for image structure and texture using anisotropic heat transfer model,” Multimedia Tools and Applications, 56(3), 469-483, 2012. DOI: 10.1007/s11042-010-0601-4 ↩︎ ↩︎ ↩︎

  5. Y. Oono and S. Puri, “Computationally efficient modeling of ordering of quenched phases,” Physical Review Letters, 58(8), 836-839, 1987. DOI: 10.1103/PhysRevLett.58.836 ↩︎ ↩︎

  6. M. Patra and M. Karttunen, “Stencils with isotropic discretization error for differential operators,” Numerical Methods for Partial Differential Equations, 22(4), 936-953, 2006. DOI: 10.1002/num.20129 ↩︎ ↩︎

  7. Aurélien Pierre, “Rotation-invariant Laplacian for 2D grids,” 2021, which derives the 9-point stencil, the B-spline Gaussian-equivalent $\sigma_B$, and the difference-of-Gaussians to Laplacian normalization constant used as B_SPLINE_TO_LAPLACIAN. URL ↩︎ ↩︎ ↩︎ ↩︎

  8. Michael Unser, “Splines: A Perfect Fit for Signal and Image Processing,” IEEE Signal Processing Magazine, 16(6), 22-38, 1999. DOI: 10.1109/79.799930 ↩︎

  9. Holger Dammertz, Daniel Sewtz, Johannes Hanika, and Hendrik P.A. Lensch, “Edge-Avoiding À-Trous Wavelet Transform for fast Global Illumination Filtering,” Ulm University, ↩︎

  10. The local-affine “color-line” prior — within a small patch, a surface’s colour channels are affinely related. Canonical sources: Ido Omer and Michael Werman, “Color Lines: Image Specific Color Representation,” CVPR 2004; and the matting Laplacian of Anat Levin, Dani Lischinski, and Yair Weiss, “A Closed-Form Solution to Natural Image Matting,” IEEE TPAMI 30(2), 228-242, 2008, DOI 10.1109/TPAMI.2007.1177 . The related “Colorization using Optimization” (SIGGRAPH 2004, DOI 10.1145/1015706.1015780 ) uses a softer intensity-affinity prior. ↩︎ ↩︎

  11. The near-saturation nonlinearity of image sensors is standard characterization knowledge : J. R. Janesick, Photon Transfer: DN → λ, SPIE Press, 2007 (gain and noise are measured at the low-illumination end because linearity degrades approaching full well) ; the EMVA 1288 standard (European Machine Vision Association) restricts its linearity regression to 0–70 % of saturation for the same reason ; F. Wang and A. J. P. Theuwissen, “Linearity analysis of a CMOS image sensor,” Electronic Imaging, 2017, details the pixel-chain mechanisms (source-follower gain, voltage-dependent photodiode capacitance). URL . The magnitude of the bias on a given camera is measured from the image itself by the knee estimator ; see the figure in the knee section. ↩︎