Отрегулируйте экспозицию изображения RAW на основе значения EV - PullRequest
0 голосов
/ 20 января 2019

Я пытаюсь написать программу, которая принимает матрицу, полную значений 12-битного датчика RAW (в диапазоне от [512-4096]) (512 - уровень черного датчика Байера -> т.е. что такое чистый черный цветопределяется как) и регулирует EV каждого пикселя, точно так же, как слайдер «Экспозиция» Adobe Camera Raw (ACR).Я пытаюсь понять, как это делается в принципе.Я посмотрел десятки блогов, которые объясняют, как рассчитывается EV, и кажется, что это:

Эта ссылка , кажется, дает формулу: PixelAdjusted = Pixel * 2 ^ EV

Это просто кажется очень неправильным, потому что корректировка в 5 EV делает изображение непропорциональным ... и я не могу найти никаких других ресурсов в Интернете.В Википедии есть отличная запись о Значение экспозиции , но, похоже, она не имеет того, что я ищу ... какая-либо помощь в этом?

Спасибо!

Вот пример того, что я имею в виду:

Файл RAW в ACR с EV 0: Before

С EV 5: After

В настоящее время у меня есть эта формула:

black_level = 512
bit_depth = 2**12
normalized = max((raw_pixel - black_level),0) / (bit_depth) ## normalize to [0,1]
exposed = normalized * (2**EV)    ## expose by desired EV value

## scale back to normal level:
raw_pixel_new = exposed * bit_depth  

Но это не работает для любого значения EV, которое не равно 5, поэтому формула неверна.Я также знаю, что эта формула неверна, потому что она не работает, если EV = 0.Я нашел десятки сайтов, которые объясняют, что формула просто new_pixel = pixel * 2^exposure, но это не работает с необработанными фотографиями ... я что-то упускаю?

Есть мысли?

Вот код Python и некоторые файлы, которые я использую для тестирования:

Код:
import rawpy
import numpy as np

from PIL import Image

bit_depth = 12
black_level = 512
exposure = 4


path = "/001_ev0.DNG"
raw = rawpy.imread(path)

im = raw.raw_image_visible
im = np.maximum(im - black_level, 0)
im *= 2**exposure
# im = im + black_level # for some reason commenting this out makes it slightly better
im = np.minimum(im,2**12 - 1)
raw.raw_image[:,:] = im
im = raw.postprocess(use_camera_wb=True,no_auto_bright=True)
img = Image.fromarray(im, 'RGB')
img.show() #This should look like the file: 001_ev4.tif
Изображения:

https://drive.google.com/open?id=1T0ru_Vid8ctM3fDdbx1hvxNojOXOzXxg

Я почему-то потратил 14 часов на это... Я понятия не имею, что я делаю неправильно, поскольку я не могу заставить его работать последовательно (с несколькими электромобилями), всегда есть либо зеленый, либо пурпурный оттенок.Я думаю, что факт, что это - СЫРЦОВАЯ фотография, трахает зеленый канал ...

1 Ответ

0 голосов
/ 20 января 2019

Предполагая, что вы начинаете с необработанного изображения, вы находитесь в линейном пространстве. В этом случае изменение экспозиции является мультипликативной операцией.

Увеличение Значение экспозиции (EV) на 1 соответствует удвоению экспозиции. Экспозиция - это линейная мера количества света, которое достигает каждого пикселя. Удвоение экспозиции удваивает количество света. Поскольку в фотографии обычно думают о долях текущей экспозиции, имеет смысл говорить об «увеличении EV на 1», а не о «умножении экспозиции на 2».

Таким образом, чтобы увеличить значение экспозиции на n , умножьте значения пикселей на 2 n .

Если входное изображение представляет собой файл JPEG или TIFF, оно, вероятно, находится в цветовом пространстве sRGB. Это нелинейное цветовое пространство, предназначенное для увеличения видимого диапазона файла 8-битного изображения. Прежде чем изменять экспозицию, необходимо сначала преобразовать sRGB в линейный RGB. Это может быть достигнуто приблизительно путем увеличения значения каждого пикселя до степени 2,2, Википедия имеет точную формулировку .


Проблемы в ОП вызваны неточным уровнем черного. raw.black_level_per_channel возвращает 528 для данного изображения (это одинаковое значение для каждого из каналов, хотя я предполагаю, что это не обязательно так для других моделей камер), а не 512. Кроме того, код записывает raw.raw_image_visible обратно в raw.raw_image, что не правильно.

Следующий код дает правильные результаты:

import rawpy
import numpy as np
from PIL import Image

bit_depth = 12
exposure = 5

path = "/001_ev0.DNG"
raw = rawpy.imread(path)
black_level = raw.black_level_per_channel[0] # assume they're all the same

im = raw.raw_image
im = np.maximum(im, black_level) - black_level # changed order of computation
im *= 2**exposure
im = im + black_level
im = np.minimum(im, 2**12 - 1)
raw.raw_image[:,:] = im
im = raw.postprocess(use_camera_wb=True, no_auto_bright=True)
img = Image.fromarray(im, 'RGB')
img.show()
...