C # изменить dpi загруженного изображения - PullRequest
7 голосов
/ 21 января 2011

Мне нужна следующая функция, которая вызывается для изменения разрешения изображения. Я хочу сделать это так, чтобы загруженное изображение, например, с разрешением 300 точек на дюйм, было изменено на 72 точки на дюйм (для Интернета). Этот вопрос связан с другим вопросом здесь, на SO, где я работаю.

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

public static byte[] SetDpiTo72(this byte[] imageToFit, string mimeType, Size newSize)
{    
    using (MemoryStream memoryStream = new MemoryStream(), newMemoryStream = new MemoryStream())
    {
        memoryStream.Write(imageToFit, 0, imageToFit.Length);
        var originalImage = new Bitmap(memoryStream);

        using (var canvas = Graphics.FromImage(originalImage))
        {
            canvas.SmoothingMode = SmoothingMode.AntiAlias;
            canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
            canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
            canvas.DrawImage((Image)originalImage,0,0, newSize.Width, newSize.Height);

            newBitmap.SetResolution(72, 72);
            newBitmap.Save(newMemoryStream, ImageFunctions.GetEncoderInfo(mimeType), null);
        }
        return newMemoryStream.ToArray();
    }
}

Упомянутый метод расширения вызывается в функции, аналогичной ситуации, приведенной ниже;

if (newSize.Width > originalImage.Width && newSize.Height > originalImage.Height)
{
     newSize.Width = originalImage.Width;
     newSize.Height = originalImage.Height;

     uploadedFileBuffer = uploadedFileBuffer.SetDpiTo72(uploadedFile.ContentType, newSize);

     return CreateFile(newSize, uploadedFile, uploadedFileBuffer);
}

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

ОБНОВЛЕНИЕ ПОСЛЕ НЕСКОЛЬКО ОТВЕТОВ:

public static byte[] SetDpiTo72(this byte[] imageToFit, string mimeType, Size newSize)
        {
            using (MemoryStream memoryStream = new MemoryStream(), newMemoryStream = new MemoryStream())
            {
                memoryStream.Write(imageToFit, 0, imageToFit.Length);
                var originalImage = new Bitmap(memoryStream);

                using (var canvas = Graphics.FromImage(originalImage))
                {
                    canvas.SmoothingMode = SmoothingMode.AntiAlias;
                    canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
                    canvas.DrawImage((Image)originalImage,0,0, newSize.Width, newSize.Height);

                    originalImage.SetResolution(72, 72);

                    var epQuality = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75);
                    var epParameters = new EncoderParameters(1);
                    epParameters.Param[0] = epQuality;

                    Image newimg = Image.FromStream(memoryStream);

                    //Getting an GDI+ exception after the execution of this line.
                    newimg.Save("C:\\test1234.jpg", ImageFunctions.GetEncoderInfo(mimeType), epParameters);

                    originalImage.Save("test.jpg", ImageFormat.Jpeg);

                    //This line give me an Argumentexception - Parameter is not valid.
                    //originalImage.Save(newMemoryStream, ImageFunctions.GetEncoderInfo(mimeType), epParameters);
                    //newMemoryStream.Close();
                }
                return newMemoryStream.ToArray();
            }
        }

Стековая трасса, которая идет с исключением, говорит мне следующее:

   at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
   at Extensions.ByteArrayExtensions.SetDpiTo72(Byte[] imageToFit, String mimeType, Size newSize) in C:\Website\Project\Extensions\ByteArrayExtensions.cs:line 356
   at CMS.Presentation.FileFunctions.CreateFullsizeImage(HttpPostedFileBase uploadedFile, Size newSize, Byte[] uploadedFileBuffer) in C:\Website\Project\CMS.Presentation\FileFunctions.cs:line 197
   at CMS.Presentation.FileFunctions.CreateFile(HttpPostedFileBase uploadedFile, INodeService nodeservice, Guid userId, Node parentNode) in C:\Website\Project\CMS.Presentation\FileFunctions.cs:line 53

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

private static Bitmap ResizeImage(Image image, int width, int height)
        {
            var frameCount = image.GetFrameCount(new FrameDimension(image.FrameDimensionsList[0]));
            var newDimensions = ImageFunctions.GenerateImageDimensions(image.Width, image.Height, width, height);
            Bitmap resizedImage;

            if (frameCount > 1)
            {
                //we have a animated GIF
                resizedImage = ResizeAnimatedGifImage(image, width, height);
            }
            else
            {
                resizedImage = (Bitmap)image.GetThumbnailImage(newDimensions.Width, newDimensions.Height, null, IntPtr.Zero);
            }

            resizedImage.SetResolution(72,72);

            return resizedImage;
        }

Ответы [ 4 ]

3 голосов
/ 02 февраля 2011

Мне понадобилось время, но я наконец нашел проблему!Проблема заключалась в функции ResizeImage, которую я использовал.В 'GetThumbnailImage', чтобы быть конкретным.Я столкнулся с другой проблемой с размытыми изображениями, которая была объяснима, потому что GetThumbnailImage растянул созданный ThumbNail до желаемого размера.И разрешение от миниатюры никогда не меняется.

private static Bitmap ResizeImage(Image image, int width, int height)
        {
            var frameCount = image.GetFrameCount(new FrameDimension(image.FrameDimensionsList[0]));
            var newDimensions = ImageFunctions.GenerateImageDimensions(image.Width, image.Height, width, height);
            Bitmap resizedImage;

            if (frameCount > 1)
            {
                //we have a animated GIF
                resizedImage = ResizeAnimatedGifImage(image, width, height);
            }
            else
            {
                resizedImage = (Bitmap)image.GetThumbnailImage(newDimensions.Width, newDimensions.Height, null, IntPtr.Zero);
            }

            resizedImage.SetResolution(72,72);

            return resizedImage;
        }

Путем изменения функции выше для функции ниже я смог решить проблему, используя Graphics.DrawImage, чтобы перерисовать новое изображение перед его рендерингом.Также GenerateImageDimensions был немного изменен.Вместе взятые, проблема была решена.

private static Bitmap ResizeImage(Image image, int width, int height)
        {
            var frameCount = image.GetFrameCount(new FrameDimension(image.FrameDimensionsList[0]));
            var newDimensions = ImageFunctions.GenerateImageDimensions(image.Width, image.Height, width, height);

            var resizedImage = new Bitmap(newDimensions.Width, newDimensions.Height);
            if (frameCount > 1)
            {
                //we have a animated GIF
                resizedImage = ResizeAnimatedGifImage(image, width, height);
            }
            else
            {

                //we have a normal image
                using (var gfx = Graphics.FromImage(resizedImage))
                {
                    gfx.SmoothingMode = SmoothingMode.HighQuality;
                    gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;

                    var targRectangle = new Rectangle(0, 0, newDimensions.Width, newDimensions.Height);
                    var srcRectangle = new Rectangle(0, 0, image.Width, image.Height);

                    gfx.DrawImage(image, targRectangle, srcRectangle, GraphicsUnit.Pixel);
                }
            }

            return resizedImage;
        }
3 голосов
/ 21 января 2011

Хорошо, я пробовал это только для файлов на жестком диске, но оно должно работать и с потоками.

        Bitmap bitmap = new Bitmap(loadFrom);
        Bitmap newBitmap = new Bitmap(bitmap);
        newBitmap.SetResolution(72, 72);
        newBitmap.Save(saveTo);
1 голос
/ 21 января 2011

Под «изменением разрешения» вы на самом деле имеете в виду, что хотите уменьшить количество пикселей в изображении на 72/300? То есть изменить изображение 4000x3000 на 960x720?

Если это так, я не вижу, где ваш код на самом деле это делает. Перегрузка DrawImage (), которую вы используете , делает это:

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

Что именно и происходит.

Попробуйте одну из других перегрузок , такую ​​как эта :

Рисует указанное изображение в указанном месте и с указанным размером.

например:

// Create image.
Image newImage = Image.FromFile("SampImag.jpg");

// Create coordinates for upper-left corner of image and for size of image.
int x = 0;
int y = 0;
int width = 450;
int height = 150;

// Draw image to screen.
e.Graphics.DrawImage(newImage, x, y, width, height);

РЕДАКТИРОВАТЬ: за комментарии, я понимаю, что ОП хочет уменьшить размер файла без уменьшения количества пикселей. Поэтому файлы должны быть повторно сжаты.

Я заимствовал пример кода из здесь :

ImageCodecInfo iciJpegCodec = null;

// This will specify the image quality to the encoder. Change the value of 75 from 0 to 100, where 100 is best quality, but highest file size.
EncoderParameter epQuality = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75);

// Get all image codecs that are available
ImageCodecInfo[] iciCodecs = ImageCodecInfo.GetImageEncoders();

// Store the quality parameter in the list of encoder parameters
EncoderParameters epParameters = new EncoderParameters(1);
epParameters.Param[0] = epQuality;

// Loop through all the image codecs
for (int i = 0; i < iciCodecs.Length; i++)
{
    // Until the one that we are interested in is found, which is image/jpeg
    if (iciCodecs[i].MimeType == "image/jpeg")
    {
        iciJpegCodec = iciCodecs[i];
        break;
    }
}

// Create a new Image object from the current file
Image newImage = Image.FromFile(strFile);

// Get the file information again, this time we want to find out the extension
FileInfo fiPicture = new FileInfo(strFile);

// Save the new file at the selected path with the specified encoder parameters, and reuse the same file name
newImage.Save(outputPath + "\\" + fiPicture.Name, iciJpegCodec, epParameters);
0 голосов
/ 21 января 2011

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

newBitmap.Save("c:\test.png", ImageFormat.Png);

Проверьте dpi для вышеупомянутогофайл из файла -> свойства -> сводка (дополнительно).Это должно быть 72 точек на дюйм.

...