Работа с изображениями JPG

Язык Visual Basic на платформе .NET.

Модераторы: Ramzes, Sebas

krukovis84
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 108
Зарегистрирован: 04.08.2009 (Вт) 11:16
Откуда: Кочевник

Работа с изображениями JPG

Сообщение krukovis84 » 01.09.2011 (Чт) 13:22

Всем привет!

Подскажите пожалуйста какими средствами можно обрезать (уменьшать высоту и ширину) файла JPG и уменьшать его качество?
В общем случае нужно программно уменьшать вес файлов JPG до 300 kB, сохраняя максимально возможное качество. Желательно чтобы эти механизмы работали на FW 2.0, т.к. юзеры предполагаются тупые, иначе бы пользовались XnView. Но если есть что то реально крутое и простое на FW 4.0 - значит научу обновлять винду. Т.к. нужна максимально простая и легкая программка с интерфейсом из двух кнопок.
Может знаете какие то примеры?

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: Работа с изображениями JPG

Сообщение FireFenix » 01.09.2011 (Чт) 18:43

если под WPF То http://msdn.microsoft.com/ru-ru/library/ms771609%28v=vs.90%29.aspx
Для WF вроде через обычный энкодер http://msdn.microsoft.com/en-us/library/system.drawing.imaging.encoder.quality.aspx для FW2.0 тоже вроде этот метод

Т.е. ты вначале битмап корректируешь как нада, а потом через экнодер с нужным качеством сохраняешь

А корректируется, как обычно - через графикс (Graphics.FromImage и пошёл :) )
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる

Qwertiy
Доктор VB наук
Доктор VB наук
 
Сообщения: 2753
Зарегистрирован: 26.06.2011 (Вс) 21:26

Сообщение Qwertiy » 01.09.2011 (Чт) 19:59

Ссылки: качество, размер.

Код:
Код: Выделить всё
Imports System.IO
Imports System.Drawing.Imaging

Module All

Public Function GetJpegContent(ByVal Pct As Image) As Byte()
  Dim File As New MemoryStream
  Pct.Save(File, ImageFormat.Jpeg)
  Return File.ToArray()
End Function

Public Function GetJpegContent(ByVal Pct As Image, ByVal Quality As Long) As Byte()
  Dim File As New MemoryStream
  Dim EncoderParams As New EncoderParameters(1)
  EncoderParams.Param(0) = New EncoderParameter(Encoder.Quality, Quality)
  Pct.Save(File, GetEncoderInfo("image/jpeg"), EncoderParams)
  Return File.ToArray()
End Function

Private Function GetEncoderInfo(ByVal MimeType As String) As ImageCodecInfo
  For Each Codec As ImageCodecInfo In ImageCodecInfo.GetImageEncoders()
    If Codec.MimeType = MimeType Then Return Codec
  Next Codec
  Return Nothing
End Function

Public Function ReduceByQuality(ByVal Pct As Image, ByVal Lim As Integer) As Byte()
  Dim LastOk() As Byte = Nothing, Res() As Byte
  Dim L As Integer = 0, R As Integer = 100, Cur As Integer

  Do While L < R
    Cur = (L + R + 1) >> 1
    Res = GetJpegContent(Pct, Cur)
    If Res.Length > Lim Then
      R = Cur - 1
    Else
      L = Cur
      LastOk = Res
    End If
  Loop

  Return LastOk
End Function

Public Function ReduceBySize(ByVal Pct As Image, ByVal Lim As Integer) As Byte()
  Dim LastOk() As Byte = Nothing, Res() As Byte
  Dim LHeight As Integer = 0, RHeight As Integer = Pct.Height, CurHeight As Integer
  Dim LWidth As Integer = 0, RWidth As Integer = Pct.Width, CurWidth As Integer

  Do While LHeight < RHeight
    CurHeight = (LHeight + RHeight + 1) >> 1
    CurWidth = (LWidth + RWidth + 1) >> 1
    Res = GetJpegContent(New System.Drawing.Bitmap(CType(Pct, Bitmap), CurWidth, CurHeight))
    If Res.Length > Lim Then
      RHeight = CurHeight - 1
      RWidth = CurWidth - 1
    Else
      LHeight = CurHeight
      LWidth = CurWidth
      LastOk = Res
    End If
  Loop

  Return LastOk
End Function

Public Sub Main()
  My.Computer.FileSystem.WriteAllBytes("ReduceByQuality.jpg", ReduceByQuality(Bitmap.FromFile("input.jpg"), 307200), False)
  My.Computer.FileSystem.WriteAllBytes("ReduceBySize.jpg", ReduceBySize(Bitmap.FromFile("input.jpg"), 307200), False)
  MsgBox("Ready")
End Sub

End Module

Остаётся добавить проверку на Nothing и соединить оба метода.

PS: Если был скопирован код из предыдущего моего сообщения (удалил), то надо исправить в нём ошибку. Правильно так:Cur = (L + R + 1) >> 1.

krukovis84
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 108
Зарегистрирован: 04.08.2009 (Вт) 11:16
Откуда: Кочевник

Re: Работа с изображениями JPG

Сообщение krukovis84 » 02.09.2011 (Пт) 14:20

Qwertiy, спасибо, офигенный пример. Спасибо!Мне почти работы не осталось :)

А в связи с тем, что я узнал про WPF - где это нашло применение? По смыслу похоже на JavaScript.
Есть какие то плюсы в применении вместо обычных WF ?

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: Работа с изображениями JPG

Сообщение FireFenix » 02.09.2011 (Пт) 15:57

krukovis84 писал(а):По смыслу похоже на JavaScript.

По смыслу похоже на HTML для проектирования дизайна форм

krukovis84 писал(а):Есть какие то плюсы в применении вместо обычных WF ?

Аппарaтное ускорение через DX, стили, и немного другой подход, а так же более гибкая система для дизайна + анимации
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる

Qwertiy
Доктор VB наук
Доктор VB наук
 
Сообщения: 2753
Зарегистрирован: 26.06.2011 (Вс) 21:26

Сообщение Qwertiy » 02.09.2011 (Пт) 19:31

krukovis84 писал(а):Qwertiy, спасибо, офигенный пример. Спасибо!Мне почти работы не осталось :)

Пожалуйста.
Не так уж мало там осталось: добавить три проверки на Nothing (хотя, можно отделаться и двумя, в рассчёте на то, что энкодер всегда существует), измененить возвращаемые значения и придумать алгоритм для совмещения методов :)

PS: Было бы интересно увидеть итоговый код.

Qwertiy
Доктор VB наук
Доктор VB наук
 
Сообщения: 2753
Зарегистрирован: 26.06.2011 (Вс) 21:26

Сообщение Qwertiy » 03.09.2011 (Сб) 15:40

Ещё подумал... Код, изменяющий размер, может немного подпортить пропорции изображения. Лучше использовать коэффициент сжатия.

krukovis84
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 108
Зарегистрирован: 04.08.2009 (Вт) 11:16
Откуда: Кочевник

Re:

Сообщение krukovis84 » 04.09.2011 (Вс) 9:34

Qwertiy писал(а):Не так уж мало там осталось: добавить три проверки на Nothing
PS: Было бы интересно увидеть итоговый код.


Имеется ввиду проверка на существование файла для двух преобразований (сущестование кодека, думаю бессмысленно проверять)?

Во всем разобрался, кроме вот этой строчки. Поясните пожалуйста ее смысл. А точнее смысл битового сдвига.
Код: Выделить всё
Cur = (L + R + 1) >> 1


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

Что имеется ввиду под "коэффициентом сжатия"? То что нужно не по одному пикселю убирать с каждой стороны, а равными частями уменьшать? Например 1% от шириниы и 1% от высоты отсекать? Если последнее, то да, я переделал на % - так работает быстрее и картинка не деформируется.

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

З.Ы. Код оптимизировал немного. Выложу по итогам работы.

Qwertiy
Доктор VB наук
Доктор VB наук
 
Сообщения: 2753
Зарегистрирован: 26.06.2011 (Вс) 21:26

Сообщение Qwertiy » 04.09.2011 (Вс) 12:44

krukovis84 писал(а):Имеется ввиду проверка на существование файла для двух преобразований (сущестование кодека, думаю бессмысленно проверять)?

Нет. Имеется в виду, что обе сжимающие функции возвращают Nothing, если изображение невозможно сжать до требуемого размера.
Формально, существование кодека проверять тоже надо, тем более, что если его нет, то можно сохранить в JPG другим способом. Но я тоже думаю, что эта проверка необязательна.

krukovis84 писал(а):Во всем разобрался, кроме вот этой строчки. Поясните пожалуйста ее смысл. А точнее смысл битового сдвига.
Код: Выделить всё
Cur = (L + R + 1) >> 1

Вся строчка - получение среднего арифметического с округлением вверх. Битовый сдвиг в ней - это целочисленное деление пополам. Можно было написать Cur = (L + R + 1) \ 2

krukovis84 писал(а):Что имеется ввиду под "коэффициентом сжатия"? То что нужно не по одному пикселю убирать с каждой стороны, а равными частями уменьшать? Например 1% от шириниы и 1% от высоты отсекать? Если последнее, то да, я переделал на % - так работает быстрее и картинка не деформируется.

Проценты использовать не совсем правильно, т. к. это ухудшает точность определения максимального размера. Остальное напишу потом.

krukovis84 писал(а):Подскажите пожалуйста где у фотографии лежит информация "Дата снимка" и подобная. При обрезке это все пропадает, а надо, чтобы обязательно сохранялась эта информация.

Не знаю.

iGrok
Артефакт VBStreets
Артефакт VBStreets
 
Сообщения: 4272
Зарегистрирован: 10.05.2007 (Чт) 16:11
Откуда: Сетевое сознание

Re: Работа с изображениями JPG

Сообщение iGrok » 04.09.2011 (Вс) 13:03

krukovis84 писал(а):Подскажите пожалуйста где у фотографии лежит информация "Дата снимка" и подобная. При обрезке это все пропадает, а надо, чтобы обязательно сохранялась эта информация.

Ключевое слово - EXIF.
label:
cli
jmp label

krukovis84
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 108
Зарегистрирован: 04.08.2009 (Вт) 11:16
Откуда: Кочевник

Re: Работа с изображениями JPG

Сообщение krukovis84 » 04.09.2011 (Вс) 18:25

iGrok писал(а):Ключевое слово - EXIF.

Сенкс. Нашел класс для работы с EXIF от Altair вот тут: http://www.codeproject.com/KB/vb/exif_reader.aspx. Пишу сюда как в копилку, ну и на пользу окружающим.

krukovis84
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 108
Зарегистрирован: 04.08.2009 (Вт) 11:16
Откуда: Кочевник

Re: Работа с изображениями JPG

Сообщение krukovis84 » 04.09.2011 (Вс) 22:01

Я почитал про Jpeg, про качество и т.д. Получается что если хочешь сохранить качество - за счет него размер лучше не уменьшать.
Поэтому качество буду единожды уменьшать до 80 (это не заметно для глаза), а дальше уменьшать вес только за счет размера.
Поэтому хитрого алгоритма не получится, если следовать условию задачи (качество должно быть по максимуму, размер - не так важен).

krukovis84
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 108
Зарегистрирован: 04.08.2009 (Вт) 11:16
Откуда: Кочевник

Re: Работа с изображениями JPG

Сообщение krukovis84 » 05.09.2011 (Пн) 20:29

Итак, выводы которые я сделал в процессе экспериментов.
Процесс уменьшения качества на столько медленный и требующий на столько значительного уменьшения качества изображения для существенного изменения размера файла, что я решил вообще не уменьшать размер фотографий за счет уменьшения качества. Даже однократное уменьшение качества фотографии увеличивает время обработки в 1,5 -2 раза. Не говоря уже про многократное уменьшение с помощью алгоритма усреднения от Qwertiy, и тем более алгоритма последовательного уменьшения с заданным шагом (которое в данном случае не рассматривается в силу непригодности).
Чтобы каждый мог проверить - приведу код.
Класс Формы с одной Кнопкой:
Код: Выделить всё
Public Class Form1

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        'создаем диалог выбора файла
        Dim OpenFile As New OpenFileDialog
        OpenFile.ShowDialog()
        'получаем полное имя файла
        Dim FullFileName As String = OpenFile.FileName
        'получаем имя файла без пути
        Dim FileName As String = OpenFile.SafeFileName
        'получаем имя папки
        Dim FolderName As String = Strings.Left(OpenFile.FileName, OpenFile.FileName.Length - FileName.Length)
        'задаем новое имя для файла
        Dim NewFileName As String = "Reduced_" & FileName
        'запоминаем текущее время
        Dim StartTime As Date = DateAndTime.Now()
        'преобразуем файл
        My.Computer.FileSystem.WriteAllBytes(FolderName & NewFileName, ReduceJpegFileBySizeAndQuality(FullFileName, 80, 307200), False)
        'определяем сколько прошло времени в секундах
        Dim intTimeDiff = DateDiff(DateInterval.Second, StartTime, DateAndTime.Now())
        'выводим по готовности
        MsgBox("Готово. Преобразование длилось " & intTimeDiff & " сек ")
    End Sub
End Class


Модуль Функций для работы с Jpeg от Qwertiy немного мной модифицированный
Код: Выделить всё
Imports System.IO
Imports System.Drawing.Imaging

Public Module JpegFunctions

    Public Function GetJpegFile(ByVal Img As Image, ByVal newWidth As Long, ByVal newHeight As Long) As MemoryStream
        'создаем под файл резервное хранилище в памяти
        Dim File As New MemoryStream
        'вписываем полученное изображение в новые границы
        Dim ResizedImg As Image
        ResizedImg = New System.Drawing.Bitmap(CType(Img, Bitmap), newWidth, newHeight)
        'сохраняем в памяти изображение в формате jpeg
        ResizedImg.Save(File, ImageFormat.Jpeg)
        'возвращаем файл
        Return File
    End Function

    Public Function GetJpegFile(ByVal Img As Image, ByVal CodecInfo As ImageCodecInfo, ByVal Quality As Long) As MemoryStream
        'создаем под файл резервное хранилище в памяти
        Dim File As New MemoryStream
        'создаем хранилище для параметров кодировки, с числом параметров 1
        Dim EncoderParams As New EncoderParameters(1)
        'помещаем в хранилище параметров параметр кодировки "Качество"
        EncoderParams.Param(0) = New EncoderParameter(Encoder.Quality, Quality)
        'сохраняем в памяти файл кодированный полученным кодеком в определенное качество
        Img.Save(File, CodecInfo, EncoderParams)
        'возвращаем файл
        Return File
    End Function

    Private Function GetCodecInfo(ByVal MimeType As String) As ImageCodecInfo
        'ищем кодек среди всех кодеков
        For Each Codec As ImageCodecInfo In ImageCodecInfo.GetImageEncoders()
            If Codec.MimeType = MimeType Then
                Return Codec
            End If
        Next Codec
        Return Nothing
    End Function
    'не используется, оставлено для коллекции
    Public Function ReduceByQuality(ByVal Img As Image, ByVal Lim As Long) As Byte()
        Dim LastOk() As Byte = Nothing
        Dim File As MemoryStream '() As Byte
        Dim L As Long = 0
        Dim R As Long = 100
        Dim Cur As Long
        'находим кодек
        Dim CodecInfo As ImageCodecInfo
        CodecInfo = GetCodecInfo("image/jpeg")
        Do While L < R
            Cur = (L + R + 1) \ 2 '"\"-целочисленное деление, "/"-деление с остатком, "MOD" - остаток от деления.
            File = GetJpegFile(Img, CodecInfo, Cur)
            If File.ToArray().Length > Lim Then
                R = Cur - 1
            Else
                L = Cur
                LastOk = File.ToArray()
            End If
        Loop

        Return LastOk
    End Function

    Public Function ReduceBySize(ByVal Img As Image, ByVal Lim As Long) As Byte()
        Dim LastOk() As Byte = Nothing
        Dim Res As MemoryStream '() As Byte
        Dim LHeight As Long = 0
        Dim RHeight As Long = Img.Height
        Dim CurHeight As Long
        Dim LWidth As Long = 0
        Dim RWidth As Long = Img.Width
        Dim CurWidth As Long

        Do While LHeight < RHeight
            CurHeight = (LHeight + RHeight + 1) \ 2
            CurWidth = (LWidth + RWidth + 1) \ 2
            Res = GetJpegFile(Img, CurWidth, CurHeight)
            If Res.ToArray().Length > Lim Then
                RHeight = CurHeight - 1
                RWidth = CurWidth - 1
            Else
                LHeight = CurHeight
                LWidth = CurWidth
                LastOk = Res.ToArray()
            End If
        Loop

        Return LastOk
    End Function

    Public Function ReduceJpegFileBySizeAndQuality(ByVal FileName As String, ByVal Quality As Long, ByVal Lim As Long) As Byte()
        'сначала уменьшаем качество
        'преобразуем файл в объект Image
        Dim Img As Image = Bitmap.FromFile(FileName)
        'Процесс уменьшения качества на столько тормозной и
        'для существенного изменения размера файла требующий настолько сильного уменьшения качества,
        'что я принял решение его не использовать вообще, кто хочет - может раскоментировать и проверить
        '============================================
        ''Находим JPEG-кодек
        'Dim Codec As ImageCodecInfo = GetCodecInfo("image/jpeg")
        ''@TODO проверка на существование кодека
        ''уменьшаем качество файла до заданной величины и запоминаем в памяти
        'Dim File As MemoryStream = GetJpegFile(Img, Codec, Quality)
        ''преобразуем полученный файл в объект Image
        'Img = Bitmap.FromStream(File)
        '=============================================
        'затем уменьшаем файл с помощью уменьшения размера до заданного веса и возвращаем результат
        Dim ReducedFile As Byte()
        ReducedFile = ReduceBySize(Img, Lim)
        '@TODO проверка на существование файла
        'возвращаем файл
        Return ReducedFile
    End Function

End Module

krukovis84
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 108
Зарегистрирован: 04.08.2009 (Вт) 11:16
Откуда: Кочевник

Re: Работа с изображениями JPG

Сообщение krukovis84 » 05.09.2011 (Пн) 21:24

Под коэффициентом сжатия это имелось ввиду?
Код: Выделить всё
    Public Function GetJpegFile(ByVal Img As Image, ByVal newWidth As Long, ByVal newHeight As Long) As MemoryStream
        'создаем под файл резервное хранилище в памяти
        Dim File As New MemoryStream
        'вычисляем новые границы
        Dim FileWidth As Integer = Img.Width
        Dim FileHeight As Integer = Img.Height
        Dim Kf = Math.Min(Math.Min(newWidth / FileWidth, newHeight / FileHeight), 1)
        Dim NewFileWidth As Integer = FileWidth * Kf
        Dim NewFileHeight As Integer = FileHeight * Kf
        'вписываем полученное изображение в новые границы
        Dim ResizedImg As Image
        ResizedImg = New System.Drawing.Bitmap(CType(Img, Bitmap), NewFileWidth, NewFileHeight)
        'сохраняем в памяти изображение в формате jpeg
        ResizedImg.Save(File, ImageFormat.Jpeg)
        'возвращаем файл
        Return File

    End Function

Qwertiy
Доктор VB наук
Доктор VB наук
 
Сообщения: 2753
Зарегистрирован: 26.06.2011 (Вс) 21:26

Сообщение Qwertiy » 05.09.2011 (Пн) 22:38

krukovis84 писал(а):Даже однократное уменьшение качества фотографии увеличивает время обработки в 1,5 -2 раза.

Во-первых, это не так, а во-вторых, Ваш код кривой.

Статистика (на файле 3072*2304 размером 3 266 616 байт):
Код: Выделить всё
╔════════════╦═══════╤═══════╗
║  Итераций  ║   8   │  все  ║
╠═════╤══════╬═══════╪═══════╣
║ Ваш │  80% ║ 7 сек │       ║
║ Ваш │  75% ║ 7 сек │       ║
║ Ваш │ none ║ 6 сек │       ║
╠═════╪══════╬═══════╪═══════╣
║ Мой │  80% ║ 3 сек │ 6 сек ║
║ Мой │  75% ║ 4 сек │ 7 сек ║
║ Мой │ none ║ 4 сек │ 7 сек ║
╚═════╧══════╩═══════╧═══════╝

А вот мой код:
Код: Выделить всё
Imports System.Drawing.Imaging
Imports System.IO

Module JpegFunctions
  Private JpegCodec As ImageCodecInfo = (From Codec As ImageCodecInfo In ImageCodecInfo.GetImageEncoders() Where Codec.MimeType = "image/jpeg" Select Codec).First

  Public Function GetJpegFile(ByVal Pct As Image, ByVal Quality As Integer) As MemoryStream
    Dim File As New MemoryStream
    Dim EncoderParams As New EncoderParameters(1)
    EncoderParams.Param(0) = New EncoderParameter(Encoder.Quality, Quality)
    Pct.Save(File, JpegCodec, EncoderParams)
    'Pct.Save(File, ImageFormat.Jpeg)
    Return File
  End Function

  Public Function ReduceJpegFileSize(ByVal Pct As Image, ByVal Quality As Integer, ByVal Lim As Integer) As Byte()
    Dim File As MemoryStream, LastOk As MemoryStream = Nothing
    Dim L As Double = 0, R As Double = 1, Cur As Double

    'For Q As Integer = 0 To 7
    For Q As Integer = 0 To Math.Log(Math.Max(Pct.Width, Pct.Height), 2)
      Cur = (L + R) / 2
      File = GetJpegFile(New System.Drawing.Bitmap(CType(Pct, Bitmap), Pct.Width * Cur, Pct.Height * Cur), Quality)
      If File.Length > Lim Then
        R = Cur
      Else
        L = Cur
        LastOk = File
      End If
    Next Q

    Return If(LastOk IsNot Nothing, LastOk.ToArray(), Nothing)
  End Function
End Module

Module All
  Public Sub Main()
    'создаем диалог выбора файла
    Dim OpenFile As New OpenFileDialog
    OpenFile.ShowDialog()
    'получаем полное имя файла
    Dim FullFileName As String = OpenFile.FileName
    'получаем имя файла без пути
    Dim FileName As String = OpenFile.SafeFileName
    'получаем имя папки
    Dim FolderName As String = Strings.Left(OpenFile.FileName, OpenFile.FileName.Length - FileName.Length)
    'задаем новое имя для файла
    Dim NewFileName As String = "Reduced_" & FileName
    'запоминаем текущее время
    Dim StartTime As Date = DateAndTime.Now()
    'преобразуем файл
    My.Computer.FileSystem.WriteAllBytes(FolderName & NewFileName, ReduceJpegFileSize(Image.FromFile(FullFileName), 80, 307200), False)
    'определяем сколько прошло времени в секундах
    Dim intTimeDiff = DateDiff(DateInterval.Second, StartTime, DateAndTime.Now())
    'выводим по готовности
    MsgBox("Готово. Преобразование длилось " & intTimeDiff & " сек ")
  End Sub
End Module

Qwertiy
Доктор VB наук
Доктор VB наук
 
Сообщения: 2753
Зарегистрирован: 26.06.2011 (Вс) 21:26

Сообщение Qwertiy » 05.09.2011 (Пн) 23:03

krukovis84 писал(а):Под коэффициентом сжатия это имелось ввиду?

Нет. В моём коде используется именно то, что имелось в виду. А в приведённом Вами - нечто непонятное, но всё-таки корректирующее пропроции, хотя и не совсем верно.

krukovis84
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 108
Зарегистрирован: 04.08.2009 (Вт) 11:16
Откуда: Кочевник

Re: Работа с изображениями JPG

Сообщение krukovis84 » 06.09.2011 (Вт) 18:24

Вот код формы:
Код: Выделить всё
Public Class Form1

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        'создаем диалог выбора файла
        Dim OpenFile As New OpenFileDialog
        OpenFile.ShowDialog()
        'получаем полное имя файла
        Dim FullFileName As String = OpenFile.FileName
        'получаем имя файла без пути
        Dim FileName As String = OpenFile.SafeFileName
        'получаем имя папки
        Dim FolderName As String = Strings.Left(OpenFile.FileName, OpenFile.FileName.Length - FileName.Length)
        'задаем новое имя для файла
        Dim NewFileName As String = "Reduced_" & FileName
        'запоминаем текущее время
        Dim StartTime As Date = DateAndTime.Now()
        'преобразуем файл
        My.Computer.FileSystem.WriteAllBytes(FolderName & NewFileName, ReduceJpegFileBySizeAndQuality(FullFileName, 80, 307200), False)
        'определяем сколько прошло времени в секундах
        Dim intTimeDiff = DateDiff(DateInterval.Second, StartTime, DateAndTime.Now())
        'выводим по готовности
        MsgBox("Готово. Преобразование длилось " & intTimeDiff & " сек ")
    End Sub
End Class


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

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: Работа с изображениями JPG

Сообщение FireFenix » 06.09.2011 (Вт) 18:35

Потому что нужно закрывать файловый поток

Считываем: Файл -> FileSrteam -> MemoryStream
Закрываем FileStream, работаем с MemoryStream, конвертируем в картинку, пережимаем, изменяем/создаём новый MemoryStream
Записываем: MemoryStream -> FileStream -> Файл
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる

krukovis84
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 108
Зарегистрирован: 04.08.2009 (Вт) 11:16
Откуда: Кочевник

Re: Работа с изображениями JPG

Сообщение krukovis84 » 06.09.2011 (Вт) 18:40

FireFenix писал(а):Потому что нужно закрывать файловый поток

Считываем: Файл -> FileSrteam -> MemoryStream
Закрываем FileStream, работаем с MemoryStream, конвертируем в картинку, пережимаем, изменяем/создаём новый MemoryStream
Записываем: MemoryStream -> FileStream -> Файл


Точняк, спасибо :D

Qwertiy
Доктор VB наук
Доктор VB наук
 
Сообщения: 2753
Зарегистрирован: 26.06.2011 (Вс) 21:26

Сообщение Qwertiy » 06.09.2011 (Вт) 19:56

FireFenix писал(а):Потому что нужно закрывать файловый поток

Необязательно. Есть другой путь.
Код: Выделить всё
My.Computer.FileSystem.WriteAllBytes(FolderName & NewFileName, ReduceJpegFileSize(Image.FromStream(New FileStream(FullFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)), 75, 307200), False)

krukovis84, какой в итоге вариант преобразования? С коэффициентом сжатия разобрались?

krukovis84
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 108
Зарегистрирован: 04.08.2009 (Вт) 11:16
Откуда: Кочевник

Re: Работа с изображениями JPG

Сообщение krukovis84 » 06.09.2011 (Вт) 20:33

Qwertiy писал(а):krukovis84, какой в итоге вариант преобразования? С коэффициентом сжатия разобрались?

Да, с коэффициентом сжатия разобрался. Спасибо большое. Но уменьшение качества решил не использовать. За счет уменьшения размера и вес хорошо падает и происходит это быстрее.

Qwertiy
Доктор VB наук
Доктор VB наук
 
Сообщения: 2753
Зарегистрирован: 26.06.2011 (Вс) 21:26

Сообщение Qwertiy » 06.09.2011 (Вт) 22:44

krukovis84 писал(а):Но уменьшение качества решил не использовать. За счет уменьшения размера и вес хорошо падает и происходит это быстрее.

1. Я же привёл код, показывающий, что это не быстрее!
2. Судя по получаемым результатам, сохранение как ImageFormat.Jpeg использует качество между 75 и 80%.
3. Меня интересует число итераций: 8 (что близко к использованию процентов) или все (точность до 1 пикселя).

Кстати, стоит добавить проверку
Код: Выделить всё
If OFD.ShowDialog() = Windows.Forms.DialogResult.OK Then


PS: Мне кажется странным, что уменьшение размера лучше, чем некоторое уменьшение качества.

krukovis84
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 108
Зарегистрирован: 04.08.2009 (Вт) 11:16
Откуда: Кочевник

Re:

Сообщение krukovis84 » 07.09.2011 (Ср) 8:26

Qwertiy писал(а):
krukovis84 писал(а):Но уменьшение качества решил не использовать. За счет уменьшения размера и вес хорошо падает и происходит это быстрее.

1. Я же привёл код, показывающий, что это не быстрее!

Ты сравнивал мой кривой код и свой оптимизированный. Ну ясен пень получилось быстрее. Попробуй из своего последнего алгоритма убрать работу с качеством и посмотри как изменится скорость.
Qwertiy писал(а):2. Судя по получаемым результатам, сохранение как ImageFormat.Jpeg использует качество между 75 и 80%.

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

Qwertiy писал(а):3. Меня интересует число итераций: 8 (что близко к использованию процентов) или все (точность до 1 пикселя).


Да нет. На столько высокая точность не нужна. Я, чтобы еще ускорить процесс ввел некий диапазон от minLim до maxLim, чтобы цикл прерывался как только вес картинки в этот диапазон попадал. Я ж для людей делаю, а человек, слава богу не совершеннен и не видит разницы в нескольких сотнях пикселей. А скорость значительно увеличивается. Вместо 11 итераций, было максимум 5 вроде, а так на 2ой-3-ей итерации выходит из цикла.

Qwertiy писал(а):Кстати, стоит добавить проверку
Код: Выделить всё
If OFD.ShowDialog() = Windows.Forms.DialogResult.OK Then

Да, пожалуй.

Qwertiy писал(а):PS: Мне кажется странным, что уменьшение размера лучше, чем некоторое уменьшение качества.


Ну так ImageFormat.Jpeg же понижает качество немного (на 80% качество должен выставлять по идее) - так что некоторое уменьшение качества присутствует. :)

Qwertiy
Доктор VB наук
Доктор VB наук
 
Сообщения: 2753
Зарегистрирован: 26.06.2011 (Вс) 21:26

Сообщение Qwertiy » 07.09.2011 (Ср) 15:48

krukovis84 писал(а):Ты сравнивал мой кривой код и свой оптимизированный. Ну ясен пень получилось быстрее. Попробуй из своего последнего алгоритма убрать работу с качеством и посмотри как изменится скорость.

А что по-вашему означает строка none в этой таблице?
Qwertiy писал(а):Статистика (на файле 3072*2304 размером 3 266 616 байт):
Код: Выделить всё
╔════════════╦═══════╤═══════╗
║  Итераций  ║   8   │  все  ║
╠═════╤══════╬═══════╪═══════╣
║ Ваш │  80% ║ 7 сек │       ║
║ Ваш │  75% ║ 7 сек │       ║
║ Ваш │ none ║ 6 сек │       ║
╠═════╪══════╬═══════╪═══════╣
║ Мой │  80% ║ 3 сек │ 6 сек ║
║ Мой │  75% ║ 4 сек │ 7 сек ║
║ Мой │ none ║ 4 сек │ 7 сек ║
╚═════╧══════╩═══════╧═══════╝
Использование ImageFormat.Jpeg не даёт выигрыша по времени. Более того, время больше, чем при использовании качества 80%, но такое же, как при 75%. Размер получаемых изображений (в пикселях) подтверждает, что используется нечто между 75 и 80%.

krukovis84 писал(а):Да нет. На столько высокая точность не нужна.

Если надо получить с точностью K пикселей, то можно использовать
Код: Выделить всё
For Q As Integer = 0 To Math.Log(Math.Max(Pct.Width, Pct.Height) / K, 2)
Хотя, K не должно быть большим.

krukovis84
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 108
Зарегистрирован: 04.08.2009 (Вт) 11:16
Откуда: Кочевник

Re: Работа с изображениями JPG

Сообщение krukovis84 » 12.09.2011 (Пн) 13:29

А что по-вашему означает строка none в этой таблице?

Я почему то подумал, что это качество в 100 попугаев. Честно признаюсь - очень бегло смотрел.
Очень хочется сказать следующее:
Спасибо, Qwertiy. Ты мне очень помог.

Qwertiy
Доктор VB наук
Доктор VB наук
 
Сообщения: 2753
Зарегистрирован: 26.06.2011 (Вс) 21:26

Сообщение Qwertiy » 12.09.2011 (Пн) 15:20

Пожалуйста.
Кстати, думаю, мне самому этот код скоро понадобится :)


Вернуться в Visual Basic .NET

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 18

    TopList