Уменьшение глубины цвета в изображении не уменьшает размер файла?

Я использую этот код, чтобы уменьшить глубину изображения:

public void ApplyDecreaseColourDepth(int offset)
{
    int A, R, G, B;

    Color pixelColor;

    for (int y = 0; y < bitmapImage.Height; y++)
    {
        for (int x = 0; x < bitmapImage.Width; x++)
        {
            pixelColor = bitmapImage.GetPixel(x, y);

            A = pixelColor.A;

            R = ((pixelColor.R + (offset / 2)) - ((pixelColor.R + (offset / 2)) % offset) - 1);

            if (R < 0)
            {
                R = 0;
            }

            G = ((pixelColor.G + (offset / 2)) - ((pixelColor.G + (offset / 2)) % offset) - 1);

            if (G < 0)
            {
                G = 0;
            }

            B = ((pixelColor.B + (offset / 2)) - ((pixelColor.B + (offset / 2)) % offset) - 1);

            if (B < 0)
            {
                B = 0;
            }

            bitmapImage.SetPixel(x, y, Color.FromArgb(A, R, G, B));
        }
    }
}

Первый вопрос: смещение, которое я даю функции, не является глубиной, верно?

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

Это код, который я использую для сохранения измененного изображения:

private Bitmap bitmapImage;

public void SaveImage(string path)
{
    bitmapImage.Save(path);
} 

6 ответов

Решение

Давайте начнем с небольшой очистки кода. Следующая картина:

R = ((pixelColor.R + (offset / 2)) - ((pixelColor.R + (offset / 2)) % offset) - 1);
if (R < 0)
{
    R = 0;
}

Это эквивалентно этому:

R = Math.Max(0, (pixelColor.R + offset / 2) / offset * offset - 1);

Таким образом, вы можете упростить вашу функцию до этого:

public void ApplyDecreaseColourDepth(int offset)
{
    for (int y = 0; y < bitmapImage.Height; y++)
    {
        for (int x = 0; x < bitmapImage.Width; x++)
        {
            int pixelColor = bitmapImage.GetPixel(x, y);

            int A = pixel.A;

            int R = Math.Max(0, (pixelColor.R + offset / 2) / offset * offset - 1);
            int G = Math.Max(0, (pixelColor.G + offset / 2) / offset * offset - 1);
            int B = Math.Max(0, (pixelColor.B + offset / 2) / offset * offset - 1);

            bitmapImage.SetPixel(x, y, Color.FromArgb(A, R, G, B));
        }
    }
}

Чтобы ответить на ваши вопросы:

  1. Правильный; смещение - это размер шагов в функции шага. Глубина для каждого компонента цвета - это исходная глубина минус log2(смещение). Например, если исходное изображение имеет глубину восемь битов на компонент (бит / с), а смещение равно 16, то глубина каждого компонента составляет 8 - log2(16) = 8 - 4 = 4 бит / с. Обратите внимание, однако, что это только указывает, сколько энтропии может удерживать каждый выходной компонент, а не сколько битов на компонент будет фактически использовано для хранения результата.
  2. Размер выходного файла зависит от сохраненной глубины цвета и используемого сжатия. Простое уменьшение количества различных значений, которые может иметь каждый компонент, не приведет к автоматическому использованию меньшего количества битов для каждого компонента, поэтому несжатое изображение не будет уменьшаться, если вы явно не выберете кодировку, которая использует меньше битов на компонент. Если вы сохраняете сжатый формат, такой как PNG, вы можете увидеть улучшение с преобразованным изображением или нет; это зависит от содержания изображения. Изображения с большим количеством плоских нетекстурированных областей, например, штриховые рисунки, увидят незначительное улучшение, тогда как фотографии, вероятно, получат заметную выгоду от преобразования (хотя и за счет качества восприятия).

Вы просто устанавливаете значения пикселей на более низкий уровень.

Например, если пиксель представлен 3 каналами по 16 бит на канал, вы уменьшаете значение цвета каждого пикселя до 8 бит на канал. Это никогда не уменьшит размер изображения, поскольку выделенные пиксели уже имеют фиксированную глубину 16 бит. Попробуйте сохранить новые значения в новом изображении с максимальной глубиной 8 бит.
Конечно, у вас будет уменьшенное изображение в байтах, но не общий размер, то есть X,Y размеры изображения останутся без изменений. То, что вы делаете, снизит качество изображения.

Сначала я хотел бы задать вам один простой вопрос:)

int i = 10;

и теперь я = я--;

это влияет на размер я? ответ нет

ты делаешь то же самое

Отображаемые индексы представлены в двух матрицах: 1 для отображения цвета и 2 для отображения изображения.

вы просто меняете значение элемента, не удаляя его, чтобы оно не влияло на размер изображения

Вы не можете уменьшить глубину цвета с Get/SetPixel. Эти методы только меняют цвет.

Кажется, вы не можете легко сохранить изображение в определенном формате пикселей, но я нашел какой-то код для изменения формата пикселей в памяти. Вы можете попробовать сохранить его, и он может работать, в зависимости от того, в каком формате вы сохраняете.

Из этого вопроса: /questions/18542479/konvertirovat-24-bitnyij-bmp-v-16-bitnyij/18542504#18542504

Он дает этот код для изменения глубины цвета:

public static Bitmap ConvertTo16bpp(Image img) {
    var bmp = new Bitmap(img.Width, img.Height, System.Drawing.Imaging.PixelFormat.Format16bppRgb555);
    using (var gr = Graphics.FromImage(bmp))
    {
        gr.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height));
    }
    return bmp;
}

Вы можете изменить PixelFormat в коде на то, что вам нужно.

Растровое изображение с определенным количеством пикселей всегда имеет одинаковый размер, потому что растровое изображение не применяет сжатие. Если вы сжимаете изображение с помощью алгоритма (например, JPEG), то уменьшенное изображение должно быть меньше.

R = ((pixelColor.R + (offset / 2)) - ((pixelColor.R + (offset / 2))

Разве это не всегда возвращает 0?

Если вы хотите уменьшить размер вашего изображения, вы можете указать другой формат сжатия при вызове Image.Save ().

Формат файла GIF, вероятно, является хорошим кандидатом, так как он лучше всего работает с непрерывными пикселями одинакового цвета (что чаще случается, когда глубина цвета мала).

JPEG отлично работает с фотографиями, но вы не увидите значительных результатов, если конвертируете 24-битное изображение в 16-битное, а затем сжимаете его с помощью JPEG из-за способа работы алгоритма (лучше сохранить 24-битные картинки в формате JPEG напрямую).

И как объяснили другие, ваш код не уменьшит размер, используемый объектом Image, если вы фактически не скопируете полученные данные в другой объект Bitmap с другим PixelFormat, таким как Format16bppRgb555,

Другие вопросы по тегам