Я наложил инвертирующий фильтр сепия. Результат инвертирования фильтра не соответствует ожидаемому.
Моя логика следующая: обрабатываемый_пиксель = np.dot (sepia_filter, original_pixel)
Это означает, что: original_pixel = np.dot (np.inverse (sepia_filter), обрабатываемый_пиксель)
Вот код, который я пробовал - я также пробовал пару других подходов, таких как изменение цвета по отдельности, а затем решение системы линейных уравнений, но получил тот же результат, поэтому я предполагаю, что не понимаю что-то важное.
Требования:
import numpy as np
from PIL import Image, ImageDraw
Код фильтра сепия:
def get_pixel_after_sepia(source, pixel, sepia_filter):
colors = np.array(source.getpixel(pixel))
colors_new = tuple(map(int, np.dot(sepia_filter,colors))) + (255,) # apply filter, transform results to ints, cut to 255
return colors_new
def sepia(source, result_name):
result = Image.new('RGB', source.size)
sepia_filter = np.array([[0.393,0.769,0.189], [0.349,0.686,0.168], [0.272,0.534,0.131]])
# for every pixel
for x in range(source.size[0]):
for y in range(source.size[1]):
new_pixel = get_pixel_after_sepia(source, (x,y), sepia_filter)
result.putpixel((x, y),new_pixel)
result.save(result_name, "JPEG")
return result
Перевернутый код сепии:
def get_pixel_before_sepia(source, pixel, inversed_sepia_filter):
colors = np.array(source.getpixel(pixel))
colors_new = tuple(map(int, np.dot(inversed_sepia_filter, colors)))+ (255,)
return colors_new
def inverse_sepia(image_with_sepia, result_file):
result = Image.new('RGB', image_with_sepia.size)
sepia_filter = np.array([[0.393,0.769,0.189], [0.349,0.686,0.168], [0.272,0.534,0.131]])
inverse_sepia_filter = np.linalg.inv(sepia_filter)
for x in range(image_with_sepia.size[0]):
for y in range(image_with_sepia.size[1]):
new_pixel = get_pixel_before_sepia(image_with_sepia, (x,y), inverse_sepia_filter)
result.putpixel((x, y),new_pixel)
result.save(result_file, "JPEG")
return result
Выполнение функций:
image = Image.open("original_image.jpg")
filtered_image = sepia(image, "filtered.jpg") # result_pixel = dot_product(Filter, origin_pixel)
image_after_filter_reversing = inverse_sepia(filtered_image,'restored.jpg' ) # result_pixel = dot_product(Filter^(-1), filtering_result_pixel)
Исходное изображение
Отфильтрованное_изображение
Image_after_filter_reversing
Я понимаю, что совершенный реверс невозможен, так как мы обрезаем результаты расчета и округляем их до int. Но я ожидаю, что изображение после реверса будет довольно близко к оригиналу. Я новичок в обработке изображений, но математическая задача выглядит для меня совершенно правильно.
Проблема в том, что алгоритм инвертирования мисепии не работает должным образом
Вы можете быть более конкретным? Потому что мы не можем запустить ваш код. Вы получаете сообщения об ошибках или результат не соответствует вашим ожиданиям?
Извините, результаты не такие, как ожидалось.
Я добавил изображения, чтобы проиллюстрировать проблему.
что произойдет, если вы подадите filtered_image
в функцию inverse_sepia
?
Это на самом деле то, что я делаю. Просто изначально была опечатка в коде - извините.
У меня появилось новое понимание. Смотрите мои комментарии под ответом Дэвида.
@mapf Спасибо за ваши идеи. Я просто пытаюсь понять, как работают фильтры, так что вы правы - у меня всегда есть оригинал. Ваши мысли были очень полезны. Я также попробую вашу идею с сохранением значений без отсечения и покажу результаты здесь, если вам интересно, как только у меня будет свободное время.
Не беспокойся! Но если я правильно понимаю ответ user3386109, моя идея тоже не сработает.
@mapf Да, как я понял из объяснения - это правда. Но мне просто интересно, какой будет результат.
Если вы заинтересованы в восстановлении изображений, посмотрите, как GAN использовались для задач аналогичного типа: DeOldify , Раскрашивание с помощью GAN
@dhanushka Спасибо за ссылки. Я просто пытаюсь выяснить, как работает фильтрация в деталях - за моим вопросом нет реального проекта, я просто изучаю. Но я буду знать, как подходить к таким проблемам в будущем :) Кстати, восстановление выглядит впечатляюще.
Теперь, когда вы исправили код, ягненка можно узнать, но происходит много вырезок. Я думаю, проблема в том, что фильтр сепии, хотя и обратим, почти единственный. Вы можете видеть в SVD, как одно из сингулярных значений намного больше, чем другие. Таким образом, небольшие изменения, такие как усечение для получения целочисленного значения (вместо этого вы можете попробовать округление, может быть немного лучше), сильно увеличиваются обратной операцией, что приводит к неточному виду реконструкции.
Это звучит как хорошее объяснение. Я попробовал код с более простыми данными, и он сработал. Итак, реальный вопрос в том, как вы заставляете это работать?
@mapf Проблема в том, что фильтр сепия плюс округление с потерями, так что на самом деле вы не можете. Лучшее, что вы можете сделать, это, вероятно, что-то вроде осторожного дизеринга к фильтру сепии, а затем добавления размытия после обратного фильтра, но мы далеко уходим от моей рулевой рубки.
Хм интересно. Это дает мне идею.
Итак, я слишком устал, чтобы попробовать это сам, но в основном то, что вы делаете, после того, как вы применили фильтр сепии к пикселю, вы сравниваете пиксель со старым, чтобы получить «фактическая» матрица, которая была применена. к нему, например, тот, который будет создавать новый пиксель без отсечения. затем вы храните эту информацию где-то. В конце концов, инверсия всех этих «фактических» преобразований должна дать вам реальную инверсию фильтра сепии.
В любом случае, теперь я также понимаю, почему это, конечно, не тривиально. Потому что либо вы не знаете фильтр сепии и должны переделывать его вручную, то есть восстановление изображения, что похоже на сверхспециализированную работу (или использовать ИИ), либо вы применили фильтр самостоятельно. В этом случае у вас также есть оригинал, поэтому вам не нужен обратный фильтр.
* Под пропорциональными уравнениями я подразумеваю, что система уравнений линейно зависима.
@StasBuzuluk они не линейно зависимы - просто очень близки. Вот почему я говорю об сингулярных значениях.
@DavidEisenstat Да, ваш ответ совершенно правильный - я просто недостаточно хорошо знаю математику, чтобы правильно ее понять. Теперь я вижу, что вы на самом деле пытались показать мне ту же проблему. Спасибо за ваш ответ и дальнейшие пояснения - если вы не возражаете - я оставлю еще один ответ, отмеченный как правильный, несмотря на то, что вы были первым, просто потому, что это немного проще для людей с небольшими знаниями в линейной алгебре, поскольку сам.
Интересный вопрос.
Ответ может быть немного разочаровывающим: фильтр сепии необратим ни теоретически, ни на практике.
Числовая матрица, используемая в коде:
0.393 0.769 0.189
0.349 0.686 0.168
0.272 0.534 0.131
Соответствующая символьная матрица:
x y z
mx my mz
nx ny nz
где x=0.393 y=0.769 z=0.189 m=0.89 n=0.69
.
Когда вы вычисляете определитель символьной матрицы, он равен нулю. Следовательно, матрица необратима, как и фильтр сепии.
Тот факт, что числовая матрица имеет ненулевой определитель, объясняется просто ограниченной точностью (3 цифры) чисел. Вычисление определителя числовой матрицы дает 0,000000121, что по существу равно 0 плюс/минус несколько ошибок округления.
В качестве примечания: умножение значения пикселя на числовую матрицу эквивалентно следующим вычислениям.
R = 0.393*r + 0.769*g + 0.189*b
G = 0.89*R
B = 0.69*R
где «rgb» — исходное значение пикселя, а «RGB» — значение пикселя сепии.
Новый Оксфордский американский словарь определяет сепию как «красновато-коричневый цвет, особенно ассоциирующийся с монохромными фотографиями 19-го и начала 20-го веков».
Ключевое слово - монохром. То, что кодирует сепия, — это кажущаяся яркость каждого пикселя изображения. Он не сохраняет никакой информации о цвете в исходном изображении. Это может показаться нелогичным, поскольку изображение сепии кажется окрашенным.
Чтобы лучше понять, попробуйте использовать следующую матрицу в своем коде.
0.291 0.569 0.140
0.291 0.569 0.140
0.291 0.569 0.140
Это преобразует изображение в изображение в градациях серого, также известное как черно-белое изображение. Как и сепия, изображение в градациях серого кодирует только видимую яркость каждого пикселя. Он не сохраняет никакой информации о цвете. Разница в том, что оттенки серого используют серый оттенок в качестве основного цвета, тогда как сепия использует коричневый оттенок в качестве основного цвета. Другие цвета, которые вы видите на изображении сепии: оранжевый, персиковый, желтый и черный — это просто разные уровни яркости коричневого в цветовом пространстве RGB.
Еще одно различие между оттенками серого и сепией заключается в том, что сепия способна кодировать больше уровней яркости. Оттенки серого имеют палитру из 256 оттенков. Сепия имеет 346 различных значений пикселей. Причина в клиппинге. Учитывая входной пиксель (255, 255, 255)
, соответствующий пиксель сепии будет (345, 307, 239)
до отсечения и (255, 255, 239)
после отсечения. Красный компонент пикселя сепии имеет 346 возможных значений до отсечения. Для каждого красного значения значения зеленого и синего пропорциональны (G=0.89*R
и B=0.69*R
).
Вот палитра сепии (минус четыре самых темных оттенка):
Следовательно, практическая проблема, с которой вы сталкиваетесь, заключается в том, что исходное цветное изображение имеет палитру из 16 миллионов цветов, тогда как палитра сепии содержит всего 346 цветов. Невозможно воссоздать исходное изображение, поскольку пиксель в изображении сепии соответствует любому из примерно 48000 возможных цветов в оригинале.
В чем проблема?