Почему я получаю артефакты квантования цвета, используя PIL? - PullRequest
0 голосов
/ 28 февраля 2020

Я использую пакет gif здесь , чтобы связать воедино сюжеты matplotlib в gifs. Тем не менее, я получаю артефакты на изображениях из-за того, что цвета ... Я не знаю формального слова, но "с пониженной выборкой".

Исходные графики выглядят так: example_image

плавные согласованные изменения цвета в цветовых картах.

Однако, когда я использую пакет для конвертации в gif, я получаю это:

gif_output

Надеюсь, вы сможете увидеть квантование / понижающую дискретизацию, которая происходит в цветах.

Код, который на самом деле выполняется, очень короткий (со строкой, которую я добавил, чтобы попробовать чтобы корректно работала цветовая коррекция, но она не работала - загруженный gif-файл с этой коррекцией, но выглядит так же, как и раньше)

def frame(func):
    """
    Decorator for a matplotlib plot function.

    Example:
    ```
    @gif.frame
    def plot(x, y):
        plt.figure(figsize=(5, 5))
        plt.scatter(x, y)
        plt.xlim((0, 100))
        plt.ylim((0, 100))
    ```
    """

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        buffer = io.BytesIO()
        func(*args, **kwargs)
        plt.savefig(buffer, format="png")
        buffer.seek(0)
        image = Image.open(buffer)
        ### ADDED IN AFTER THE FACT ###
        image = image.convert('RGBA', palette=Image.ADAPTIVE)
        ### ### ### ### ### ### ### ###
        plt.close()
        return image

    return wrapper


def save(frames, path, duration=100):
    """
    Save decorated frames to an animated gif.

    - frames (list): collection of frames built with the frame decorator
    - path (str): filename with relative or absolute path
    - duration (int): milliseconds between frames
    """
    frames[0].save(
        path,
        save_all=True,
        append_images=frames[1:],
        optimize=True,
        duration=duration,
        loop=0,
    )

Любая идея, что я могу сделать с пакетом PIL, который будет поддерживать непрерывность цветовых карт при записи в GIF-файлы? Должен ли размер dpi / file-size быть очень большим, чтобы поддерживать это?

Вот код, который создал график, на всякий случай:

@gif.frame
def plot_signal_and_ft(t, t_step):
    ### Setup ###
    dims = (501,501)
    x = np.linspace(0,100,dims[0])
    y = np.linspace(0,100,dims[1])
    u = float(100/dims[0])
    cen = [50.0, 50.0]
    xx, yy = np.meshgrid(x, y, sparse=True)
    signal = np.zeros(dims)
    r = 25*(0.003125*np.exp(5.66643*(t/t_step))+0.046875)
    offset = 1.0 + ((25.0-1.0)/(t_step))*t
    pos1 = np.sqrt((xx-cen[0]+offset)*(xx-cen[0]+offset)+(yy-cen[1])*(yy-cen[1]))
    pos2 = np.sqrt((xx-cen[0]-offset)*(xx-cen[0]-offset)+(yy-cen[1])*(yy-cen[1]))
    sigma, mu = r, 0
    signal += np.exp(-( (pos1-mu)**2 / ( 2.0 * sigma**2 ) ) )
    signal += np.exp(-( (pos2-mu)**2 / ( 2.0 * sigma**2 ) ) )

    # FT
    F = np.fft.fftshift(np.fft.fft2(signal))
    max_real = 10.0
    max_imag = 3.0

    ### PLOTTING ###
    f, ax = plt.subplots(figsize=(6, 6), nrows=2, ncols=2, dpi=100)
    # SIGNAL 
    sig = ax[0,0].imshow(signal, cmap = plt.cm.Greys, vmin=0, vmax=1, origin="lower")
    ax[0,0].axis("off")
    ax[0,0].set_title("Signal", fontsize="medium", fontweight="bold",fontfamily="serif")
    divider = make_axes_locatable(ax[0,0])
    cax = divider.append_axes("right", size="5%", pad=0.1)
    cbar = f.colorbar(sig, cax=cax)
    cbar.set_ticklabels(["0.0", "0.2", "0.4", "0.6", "0.8", "1.0"])
    # REAL
    real = ax[1,0].imshow(np.real(F), cmap=mpl.cm.RdBu, clim=(-max_real, max_real), 
                        norm=MidpointNormalize(midpoint=0.0, vmin=-max_real, vmax=max_real), 
                        origin="lower")
    ax[1,0].axis("off")
    ax[1,0].set_title("R[FT]", fontsize="medium", fontweight="bold",fontfamily="serif")
    divider = make_axes_locatable(ax[1,0])
    cax = divider.append_axes("right", size="5%", pad=0.1)
    f.colorbar(real, cax=cax)
    # IMAG
    imag = ax[1,1].imshow(np.imag(F), cmap=mpl.cm.PiYG, clim=(-max_imag, max_imag), 
                        norm=MidpointNormalize(midpoint=0.0, vmin=-max_imag, vmax=max_imag), 
                        origin="lower")
    ax[1,1].axis("off")
    ax[1,1].set_title("Im[FT]", fontsize="medium", fontweight="bold",fontfamily="serif")
    divider = make_axes_locatable(ax[1,1])
    cax = divider.append_axes("right", size="5%", pad=0.1)
    f.colorbar(imag, cax=cax)
    # MAGNITUDE
    mag = ax[0,1].imshow(np.absolute(F), cmap=plt.cm.hot, clim=(0, max_real), 
                     norm=MidpointNormalize(midpoint=max_real/2, vmin=0, vmax=max_real), 
                     origin="lower")
    ax[0,1].axis("off")
    ax[0,1].set_title("||FT||", fontsize="medium", fontweight="bold",fontfamily="serif")
    divider = make_axes_locatable(ax[0,1])
    cax = divider.append_axes("right", size="5%", pad=0.1)
    f.colorbar(mag, cax=cax)

    plt.tight_layout()

t_steps = 10
frames = []
traj = [t for t in range(t_steps)] + [t_steps-t for t in range(t_steps)]
for t in traj:
    frame = plot_signal_and_ft(t, t_steps)
    frames.append(frame)

gif.save(frames, "./test_double.gif", duration=150)

1 Ответ

1 голос
/ 02 марта 2020

Я даже не понимаю половину вашего кода, поэтому я прошу прощения, если мой ответ каким-либо образом неправильный, но у меня достаточно хорошее понимание цифровых изображений.

К сожалению, формат GIF поддерживает только 256 цветовых вариаций , который никогда не даст достойного градиента. Вы можете изучить некоторые параметры, такие как сглаживание изображения (доступно в формате pil image.convert https://pillow.readthedocs.io/en/stable/reference/Image.html), но при работе с .gif вы в основном застряли с таким качеством. Существуют некоторые сложные решения, позволяющие получить больше цветов в формате .gif, но их размер значительно увеличится, и эти решения у меня над головой, поэтому я позволю вам изучить их (см. ПРИМЕЧАНИЕ: https://techterms.com/definition/gif) .

Я бы посоветовал попытаться выяснить, можно ли использовать другой формат для своего проекта. Но опять же, я даже не уверен, что именно вы делаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...