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

У меня есть набор изображений, на которые я программно рисую простой водяной знак, используя System.Windows и System.Windows.Media.Imaging (да, не с помощью GDI+), следуя инструкциям в здесь.

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

Например, изображение размером 440 КБ становится 8,33 МБ после применения водяного знака описанным ниже способом, и это меня шокирует.

private static BitmapFrame ApplyWatermark(BitmapFrame image, string waterMarkText) {
    const int x = 5;
    var y = image.Height - 20;
    var targetVisual = new DrawingVisual();
    var targetContext = targetVisual.RenderOpen();
    var brush = (SolidColorBrush)(new BrushConverter().ConvertFrom("#FFFFFF"));
    brush.Opacity = 0.5;
    targetContext.DrawImage(image, new Rect(0, 0, image.Width, image.Height));
    targetContext.DrawRectangle(brush, new Pen(), new Rect(0, y, image.Width, 20));
    targetContext.DrawText(new FormattedText(waterMarkText, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
                             new Typeface("Batang"), 13, Brushes.Black), new Point(x, y));
    targetContext.Close();
    var target = new RenderTargetBitmap((int)image.Width, (int)image.Height, 96, 96, PixelFormats.Default);
    target.Render(targetVisual);
    var targetFrame = BitmapFrame.Create(target);
    return targetFrame;
}

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

Есть ли какие-либо настройки, которые мне здесь не хватает, чтобы сказать моей программе, чтобы качество оставалось таким же, как у исходного изображения? Как я могу предотвратить значительное изменение размера изображения после изменений в моем методе ApplyWatermark?

Изменить

1. Вот как я конвертирую BitmapFrame в Stream. Затем я использую этот Stream, чтобы сохранить изображение на AmazonS3.

private Stream EncodeBitmap(BitmapFrame image) {
    BitmapEncoder enc = new BmpBitmapEncoder();
    enc.Frames.Add(BitmapFrame.Create(image));
    var memoryStream = new MemoryStream();
    enc.Save(memoryStream);
    return memoryStream;
}

2. Вот как я получаю BitmapFrame от Stream

private static BitmapFrame ReadBitmapFrame(Stream stream) {
    var photoDecoder = BitmapDecoder.Create(
        stream,
        BitmapCreateOptions.PreservePixelFormat,
        BitmapCacheOption.None);
    return photoDecoder.Frames[0];
}

3. Вот как я читаю файл из локального каталога

public Stream FindFileInLocalImageDir() {
    try {
        var path = @"D:\Some\Path\Image.png";
        return !File.Exists(path) ? null : File.Open(path, FileMode.Open, FileAccess.Read,  FileShare.Read);
        } catch (Exception) {
            return null;
    }
}

person user3332579    schedule 22.02.2014    source источник
comment
формат изображения — .png, и изображение также будет сохранено как .png. Образ будет записан в AmazonS3 после обработки. Существует другой метод, который преобразует BitmapFrame в Stream, а затем я сохраняю поток в Amazon, используя API AmazonS3 для .net.   -  person user3332579    schedule 23.02.2014
comment
Может ли здесь иметь значение преобразователь BitmapFrame в Stream? я должен также опубликовать этот код?   -  person user3332579    schedule 23.02.2014
comment
Я отредактировал вопрос.   -  person user3332579    schedule 23.02.2014
comment
Используйте BmpBitmapEncoder() только в том случае, если вам нравятся большие файлы. Используйте Png или Jpeg, они сжимают данные изображения.   -  person Hans Passant    schedule 23.02.2014
comment
Я использовал PngBitmapEncoder, так как формат выходного изображения — png. однако недавно я заметил, что размер изображения меняется после применения водяного знака. например, изображение 960x1280 становится 1280x1706 при применении водяного знака, хотя я указываю установить ширину и высоту целевого кадра такими же, как размеры исходного изображения. может быть, это причина того, почему размер изображения становится больше? как сохранить исходный размер? что мне не хватает? пожалуйста помоги.   -  person user3332579    schedule 23.02.2014


Ответы (2)


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

Я не проверял это сам, но вы пробовали другой кодировщик?

var pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(ApplyWatermark(null, null));

MemoryStream stm = File.Create(image);
pngEncoder.Save(stm);
return stm;
person Grant Winney    schedule 23.02.2014
comment
Я пробовал PngBitmapEncoder и получил The codec cannot use the type of stream provided ошибку. - person user3332579; 23.02.2014
comment
ошибка выше больше не проблема, решена. но кажется, что я где-то ошибаюсь, поэтому размер изображения меняется после применения водяного знака. например, изображение 960x1280 становится 1280x1706 после водяного знака. ты хоть представляешь, где моя ошибка? - person user3332579; 23.02.2014
comment
DPI исходного изображения равен 72 как в x, так и в y. но, как вы видите, я устанавливаю dpi на 96 при построении выходного изображения. это также увеличение на 33%. это скорее причина? Когда я устанавливаю DPI выходного изображения на 72 (то же, что и исходное), то изображение рисуется в исходном размере, однако, как ни странно, изображение имеет некоторое свободное пространство (33%) с правой и нижней сторон. пример здесь - person user3332579; 24.02.2014
comment
но когда я устанавливаю DPI на 96, изображение заполняется до границ, но размеры выше, чем я ожидаю, как и размер. - person user3332579; 24.02.2014

Проблема в том, что при редактировании изображения пропало сжатие. Для JPG 730x1108 с размером диска 433 КБ с 32-битной (вы упомянули прозрачность, поэтому ARGB) потребуется не менее 730 * 1108 * 4 = 3,09 МБ на диске. Конечно, вы можете потом снова сжать его (для диска, сетевого потока или чего-то еще).

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

Вывод: Вам понадобится свободная память для работы с образом. В противном случае невозможно иметь его полностью под рукой.

person ZoolWay    schedule 22.02.2014
comment
Спасибо, но я не уверен, правильно ли я Вас понял. Что вы имеете в виду под free memory to work with the image? У меня нет Out of memory exception. У меня нет никаких исключений, но размер изображения действительно огромен. Итак, нужно ли сжимать изображение после обработки и перед сохранением? - person user3332579; 23.02.2014
comment
Я просто говорю, что для изменения изображения оно должно быть несжатым. Огромный размер - это реальный размер без сжатия. Вы можете повторно сжать после внесенных изменений (jpg, png и т. д.) - person ZoolWay; 24.02.2014
comment
он говорит, что большинство форматов изображений JPEG, PNG, GIF используют сжатие изображения для уменьшения размера файла, это то, что делают кодировщик и декодер, поэтому вы открываете сжатое изображение, которое распаковывает его до полного размера, а затем сохраняете его снова без применения к нему компрессии. вам нужно выбрать кодировщик, такой как JPEG или PNG, который использует сжатие файлов - person MikeT; 10.08.2016