Предполагая, что вы начинаете с необработанного изображения, вы находитесь в линейном пространстве. В этом случае изменение экспозиции является мультипликативной операцией.
Увеличение значения экспозиции (EV) на 1 соответствует удвоению экспозиции. Экспозиция - это линейная мера количества света, которое достигает каждого пикселя. Удвоение экспозиции удваивает количество света. Поскольку в фотографии обычно думают о долях текущей экспозиции, имеет смысл говорить об «увеличении EV на 1», а не о «умножении экспозиции на 2».
Таким образом, действительно, чтобы увеличить значение экспозиции на n , умножьте значения пикселей на 2 n sup>.
Если входное изображение представляет собой файл JPEG или TIFF, скорее всего, оно находится в цветовом пространстве sRGB. Это нелинейное цветовое пространство, предназначенное для увеличения видимого диапазона файла 8-битного изображения. Прежде чем изменять экспозицию, необходимо сначала преобразовать sRGB в линейный RGB. Это может быть достигнуто приблизительно путем увеличения значения каждого пикселя до степени 2,2, Википедия имеет точную формулировку .
Проблемы в ОП вызваны неточным уровнем черного. raw.black_level_per_channel
возвращает 528 для данного изображения (это одинаковое значение для каждого из каналов, хотя я полагаю, что это не обязательно имеет место для других моделей камер), а не 512. Кроме того, код записывает raw.raw_image_visible
обратно в [113 ], что не правильно.
Следующий код дает правильные результаты:
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()
Важная вещь состоит в том, чтобы попытаться кодировать к интерфейсам, и Ваших классов принимают экземпляры тех интерфейсов, а не создают сами экземпляры. Можно, очевидно, сойти с ума с этим, но это - общая хорошая практика независимо от поблочного тестирования или DI.
Например, если у Вас есть Объект Доступа к данным, Вы могли бы быть склонны записать основу для всех ДАО как это:
public class BaseDAO
{
public BaseDAO(String connectionURL,
String driverName,
String username, String password)
{
// use them to create a connection via JDBC, e.g.
}
protected Connection getConnection() { return connection; }
}
Однако было бы лучше удалить это из класса в пользу интерфейса
public interface DatabaseConnection
{
Connection getConnection();
}
public class BaseDAO
{
public BaseDAO(DatabaseConnection dbConnection)
{
this.dbConnection = dbConnection;
}
protected Connection getConnection() { return dbConnection.getConnection(); }
}
Теперь, можно обеспечить multilple реализации DatabaseConnection
. Даже игнорируя поблочное тестирование, если мы предполагаем, что используем JDBC, существует два способа получить a Connection
: пул соединения от контейнера, или непосредственно через использование драйвера. Теперь, Ваш код ДАО не связан ни с одной стратегией.
Для тестирования можно сделать a MockDatabaseConnection
это соединяется с некоторыми, встроил реализацию JDBC с консервированными данными для тестирования кода.