Как изменить размер и изображение и сохранить как байтовый массив - PullRequest
1 голос
/ 15 марта 2020

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

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

Я обычно получаю эту ошибку в функции SaveImage () в строке Image.Save (mStream, TheImage.RawFormat) System.ArgumentNullException: 'Значение не может быть нулевым. Имя параметра: encoder '

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

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

Imports System.Drawing.Imaging
Imports System.IO

Module Extentions
    <Runtime.CompilerServices.Extension()>
    Public Function GetBytes(ByRef bc As Image, Optional Format As ImageFormat = Nothing) As Byte()
        'Converts Image to Byte Array
        If Format Is Nothing Then Format = ImageFormat.Jpeg
        Using mStream As New MemoryStream()
            bc.Save(mStream, Format)
            Return mStream.ToArray()
        End Using
    End Function

    <Runtime.CompilerServices.Extension()>
    Public Function ReSize(ByRef bc As Image, MaxWidth As Integer, MaxHeight As Integer) As Image
        'Resize image to fit within MaxWidth & MaxHeight while keeping aspect ratio
        If MaxWidth < bc.Width Or MaxHeight < bc.Height Then
            Dim Scale As Double = Math.Min(MaxWidth / bc.Width, MaxHeight / bc.Height)
            Dim NewWidth As Integer = CInt(Math.Round(bc.Width * Scale))
            Dim NewHeight As Integer = CInt(Math.Round(bc.Height * Scale))
            Return New Bitmap(bc, New Size(NewWidth, NewHeight))
        Else
            Return bc
        End If
    End Function
End Module

Теперь мой код в той форме, над которой я работал, намного проще ..

    Private Sub SelectImage_Click(sender As Object, e As EventArgs) Handles SelectImage.Click
        Dim Popup As New OpenFileDialog
        With Popup
            .Filter = "Images|*.gif;*.png;*.jpg;*.bmp"
            .Title = "Select Image"
            .FileName = ""
            .FilterIndex = 1
            .Multiselect = False
            .RestoreDirectory = True
        End With

        If Popup.ShowDialog = vbOK Then
            'Load Image from disk
            OriginalImage = Image.FromFile(Popup.FileName)

            'Resize and display image on form
            Picture.Image = OriginalImage.ReSize(200, 150)

            'Save Large Image
            FullImage = OriginalImage.ReSize(400, 300).GetBytes

            'Save Small Image
            SmallImage = OriginalImage.ReSize(100, 75).GetBytes

            ImageSelected = True
        End If
    End Sub

Вот мой оригинальный код ...

    Private OriginalImage As Image
    Private FullImage As Byte()
    Private SmallImage As Byte()
    Private ImageSelected As Boolean
Private Sub SelectImage_Click(sender As Object, e As EventArgs) Handles SelectImage.Click
        Dim Popup As New OpenFileDialog
        With Popup
            .Filter = "Images|*.gif;*.png;*.jpg;*.bmp"
            .Title = "Select Image"
            .FileName = ""
            .FilterIndex = 1
            .Multiselect = False
            .RestoreDirectory = True
        End With

        If Popup.ShowDialog = vbOK Then
            'Load Image from disk
            OriginalImage = Image.FromFile(Popup.FileName)

            'Resize and display image on form
            Picture.Image = ResizeImage(OriginalImage, 200, 150)

            'Save Large Image
            Dim ResizedImage As Image = ResizeImage(OriginalImage, 400, 300)
            FullImage = SaveImage(ResizedImage)

            'Save Small Image
            ResizedImage = ResizeImage(OriginalImage, 100, 75)
            SmallImage = SaveImage(ResizedImage)

            ImageSelected = True
        End If
    End Sub

    Private Function ResizeImage(ByRef InputImage As Image, MaxWidth As Integer, MaxHeight As Integer) As Image
        'ReSize the Image if needed to save space in the database
        If MaxWidth < InputImage.Width Or MaxHeight < InputImage.Height Then
            'ReSize Image
            Dim Scale As Double = Math.Min(MaxWidth / InputImage.Width, MaxHeight / InputImage.Height)
            Return New Bitmap(InputImage, New Size(Math.Round(InputImage.Width * Scale), Math.Round(InputImage.Height * Scale)))
        Else
            'Image size was OK
            Return InputImage
        End If
    End Function

    Private Function GetImage(ImageData As Byte()) As Image
        'Converts Byte Array to Image
        Using mStream As New MemoryStream(ImageData)
            Return Image.FromStream(mStream)
        End Using
    End Function

    Private Function SaveImage(TheImage As Image) As Byte()
        'Converts Image to Byte Array
        Using mStream As New MemoryStream()
            TheImage.Save(mStream, TheImage.RawFormat)
            Return mStream.ToArray()
        End Using
    End Function


    'These are functions I was playing around with because I was getting
    'System.ArgumentNullException: 'Value cannot be null. Parameter name: encoder'
    'in the SaveImage() function on TheImage.Save(mStream, TheImage.RawFormat)
    'and I wasn't using the encoder parameter. I couldn't get these to help me though.

    Private Function SaveImage2(TheImage As Image) As Byte()
        'Converts Image to Byte Array
        Dim CodecInfo As Imaging.ImageCodecInfo = GetEncoderInfo("image/jpeg")
        Dim Parameters As New Imaging.EncoderParameters(1)
        Parameters.Param(0) = New Imaging.EncoderParameter(Imaging.Encoder.Quality, 75L)
        Using mStream As New MemoryStream()
            TheImage.Save(mStream, CodecInfo, Parameters)
            Return mStream.ToArray()
        End Using
    End Function

    Private Function GetEncoderInfo(MimeType As String) As Imaging.ImageCodecInfo
        Dim encoders As Imaging.ImageCodecInfo()
        encoders = Imaging.ImageCodecInfo.GetImageEncoders()
        For j As Integer = 0 To encoders.Length - 1
            If encoders(j).MimeType = MimeType Then Return encoders(j)
        Next
        Return Nothing
    End Function

1 Ответ

2 голосов
/ 16 марта 2020

Когда вы создаете новое растровое изображение с этим конструктором:

new Bitmap([OriginalImage], [Width], [Height])

, вы можете подумать, что формат [OriginalImage] принят во внимание. Это не. Создается новое необработанное растровое изображение с указанным размером, и в новом контейнере создается [OriginalImage].
Новый растровый объект создается в памяти, и его ImageFormat равно ImageFormat.MemoryBmp.
У этого ImageFormat нет кодера / декодера.

Когда вы сохраняете изображение с помощью перегрузки метода Image.Save (Stream, Format) , передавая новый растровый объект RawFormat, вы передаете ImageFormat.MemoryBmp.
. Затем класс изображения пытается получить кодировщик из указанного ImageFormat (используя внутренний метод FindEncoder () ), который, конечно, возвращает null, вызывая исключение:

System.ArgumentNullException: 'Значение не может быть нулевым. Имя параметра: encoder '

Вы можете протестировать его самостоятельно, пытаясь получить кодировщик / декодер из объектов OriginalImage и ResizedImage (используя ImageCodecInfo.GetImageDecoders ( ) или ImageCodecInfo.GetImageEncoders () ):

Dim originalCodec As ImageCodecInfo = ImageCodecInfo.GetImageDecoders().
    FirstOrDefault(Function(cc) cc.FormatID.Equals(ResizedImage.RawFormat.Guid))

Вы увидите, что при попытке получить кодировщик / декодер для формата ResizedImage, originalCodec будет нулевым (nothing), в то время как он возвратит кодировщик / декодер для формата OriginalImage.

Поскольку предопределенным ImageFormat является ImageFormat.Png, вы можете изменить свой метод, чтобы установить этот формат при сохранении изображения на MemoryStream:

Using mStream As New MemoryStream()
    TheImage.Save(mStream, ImageFormat.Png)
    Return mStream.ToArray()
End Using

Что-то, что является также выполняется внутри более чем один раз.
Например: Image.Save (поток MemoryStream)

...