Работа с GDI+. Проблема: Gaussian Blur

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

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

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Работа с GDI+. Проблема: Местная очистка

Сообщение Nord777 » 11.02.2009 (Ср) 12:15

Т.е. с прямоугольными областями все не так уж и сложно оказывается. А как к этому "прикрутить" очистку по региону?
Пример очистки по GraphicsPath
Код: Выделить всё
   Private Sub Clear(ByRef SrcBmp As Bitmap, ByVal ClearRegion As GraphicsPath)
      Dim NewBitmap As Bitmap
      Dim s As Size = SrcBmp.Size
      ReDim BmpArray(SrcBmp.Width - 1, SrcBmp.Height - 1)

      If GCHRGB.IsAllocated Then GCHRGB.Free()
      GCHRGB = GCHandle.Alloc(BmpArray, GCHandleType.Pinned)
      NewBitmap = New Bitmap(s.Width, s.Height, s.Width * 4, PixelFormat.Format32bppArgb, GCHRGB.AddrOfPinnedObject)

      Using G As Graphics = Graphics.FromImage(NewBitmap)
         G.DrawImage(SrcBmp, 0, 0)
      End Using

      For Row As Integer = 0 To s.Height - 1
         For Col As Integer = 0 To s.Width - 1
            If ClearRegion.IsVisible(Col, Row) Then BmpArray(Row, Col) = 0
         Next
      Next

      SrcBmp = NewBitmap
   End Sub
У регионов тоже есть IsVisible
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5

MIT
Мега гуру
Мега гуру
Аватара пользователя
 
Сообщения: 2211
Зарегистрирован: 17.09.2006 (Вс) 22:46

Re: Работа с GDI+. Проблема: Местная очистка

Сообщение MIT » 11.02.2009 (Ср) 17:42

Nord777 писал(а):Пример очистки по GraphicsPath
Спасибо огромное! :cyclops:

Nord777 писал(а):6) Сожми форму до минимальных размеров и снова растяни(Возможно несколько раз). Посмотри что стало с картинкой.
7) Нажми Button2.
Посмотрел. Кошмар. Нажал
Nord777 писал(а):Посмотри цифру в Label1
Интересно...
Nord777 писал(а):8.) Прочитай статью о GC в .NET
Прочитал, спасибо за ссыль.
Но в твоем коде предусмотрена ошибка (недоработка) - подразумевается обработка только одного изображения. С несколькими уже не прокатит:
Код: Выделить всё
Imports System.Runtime.InteropServices
Imports System.Drawing.Imaging
Imports System.Drawing.Drawing2D

Public Class Form1
    Dim bmpA() As Bitmap

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ReDim bmpA(4)

        For i As Integer = 0 To bmpA.Length - 1
            bmpA(i) = New Bitmap(100, 100)
            Using g As Graphics = Graphics.FromImage(bmpA(i))
                With g
                    .Clear(Color.CornflowerBlue)
                    .DrawRectangle(Pens.Black, 0, 0, bmpA(i).Width - 1, bmpA(i).Height - 1)
                End With
            End Using
        Next
    End Sub

    Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        For i As Integer = 0 To bmpA.Length - 1
         e.Graphics.DrawImageUnscaled(   bmpA(i),(bmpA(i).Width+10)*i+10,20)
        Next
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        For i As Integer = 0 To bmpA.Length - 1
            Clear(bmpA(i), New Rectangle(20, 20, 50, 50))
        Next
        Invalidate()
    End Sub

    Dim BmpArray(,) As Integer
    Dim GCHRGB As GCHandle

    Private Sub Clear(ByRef SrcBmp As Bitmap, ByVal ClearRect As Rectangle)
        Dim NewBitmap As Bitmap
        Dim s As Size = SrcBmp.Size
        ReDim BmpArray(s.Width - 1, s.Height - 1)

        If GCHRGB.IsAllocated Then GCHRGB.Free()
        GCHRGB = GCHandle.Alloc(BmpArray, GCHandleType.Pinned)
        NewBitmap = New Bitmap(s.Width, s.Height, s.Width * 4, PixelFormat.Format32bppArgb, GCHRGB.AddrOfPinnedObject)

        Using G As Graphics = Graphics.FromImage(NewBitmap)
            G.DrawImage(SrcBmp, 0, 0)
        End Using

        For Row As Integer = ClearRect.X To ClearRect.Width + ClearRect.X
            For Col As Integer = ClearRect.Y To ClearRect.Height + ClearRect.Y
                BmpArray(Row, Col) = 0
            Next
        Next

        SrcBmp = NewBitmap
    End Sub
End Class
Соответственно есть необходимость в копировании массива картинки, для создания нового Bitmap`а
Изображение
You can change your face, but can`t change your mind. No matter what you do.
Создайте еще более понятный интерфейс и мир создаст еще более тупого юзера. (с) Баш

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Работа с GDI+. Проблема: Местная очистка

Сообщение Nord777 » 11.02.2009 (Ср) 18:16

Но в твоем коде предусмотрена ошибка (недоработка) - подразумевается обработка только одного изображения. С несколькими уже не прокатит:
Да, присланный мною код, действительно не предполагает нескольких картинок, моей целью было указать тебе на более серьезную(и не для всех очевидную) ошибку, касающуюся сборки мусора.
А чтобы пример работал с несколькими картинками, надо всего лишь обьявить массив обьектов класса GCHandle
и передавать нужный его элемент в процедуру clear.
Вариантов - море, главное понимать свои действия.
Я думаю у тебя все получится, удачи. :)
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5

MIT
Мега гуру
Мега гуру
Аватара пользователя
 
Сообщения: 2211
Зарегистрирован: 17.09.2006 (Вс) 22:46

Re: Работа с GDI+. Проблема: Местная очистка

Сообщение MIT » 13.02.2009 (Пт) 13:45

Nord777 писал(а):А чтобы пример работал с несколькими картинками, надо всего лишь обьявить массив обьектов класса GCHandle
Есть и более универсальные методы, в которых в результате работы нет привязки к GCHandle вообще :cyclops:
Nord777 писал(а):Вариантов - море, главное понимать свои действия.
Понимание приходит постепенно. Но ведь таки приходит!
Nord777 писал(а):Я думаю у тебя все получится, удачи. :)
Спасибо тебе, прям что б я без тебя делал :cyclops:

_____________________________________________
Хотелось бы задать теоретический вопрос: какой из способов прорисовки быстрее?
Объект работы - Листвью
1 способ: создается буфферная картинка, на которую рисуются отображаемые элементы (причем целиком, например с 4 по 11, независимо от их отображаемой части), затем она отрисовывается в Paint`е учитывая сдвиг. При промотке на невидимый элемент фоновая картинка "проматывается" на элемент выше (ниже) и снизу (сверху) прорисовывается еще один элемент, после чего все повторяется.
При изменении одного элемента (если он отображается) он перерисовывается на буфферной картинке (потом вызывается Invalidate(Rect))
2 способ: Создается буфферный Graphics (альтернативный BufferedGrahics`у, самодельный), в который прорисовываются элементы с учетом сдвига (ссответственно буфферный Graphics имеет размеры контрола). При промотке вызывается Translate(X,Y), и дорисовывается недостоющая часть. В Paint`е вызывается прцедура Graphics.Render(e.Graphics).

"За" и "Против":
1 способ: Смущает рисование картинки с заданных координат, хотя там вроде BitBlt используется, но сомнения все же остаются. Да и вооюще способ кажется "наколеночным"
2 способ: Думаю, что постоянные трансляции будут очень тормозить всю конструкцию, да и при необходимости перерисовки 2-3х ранее не видимых пикселей придется перерисовывать весь Item, что, ИМХО, вообще сумашествие. Да там и математики много получается...

Используется первый способ, но начав скланяться ко второму я нашел в нем несколько, по моему, узких мест. Есть ли смысл переписывать отрисовку, или лучше все равно не получиться?
Изображение
You can change your face, but can`t change your mind. No matter what you do.
Создайте еще более понятный интерфейс и мир создаст еще более тупого юзера. (с) Баш

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Работа с GDI+. Проблема: Оптимизация

Сообщение Nord777 » 13.02.2009 (Пт) 14:51

Есть и более универсальные методы, в которых в результате работы нет привязки к GCHandle вообще
Несомненно. И возразить то нечем :D

Описание двух способов ниасилил, как-то запутанно.
Единственно, что могу посоветовать:
Создавай процедуру тестирования и замеряй время.
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5

Joo
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 762
Зарегистрирован: 14.08.2008 (Чт) 11:55
Откуда: Казахстан

Re: Работа с GDI+. Проблема: Оптимизация

Сообщение Joo » 15.02.2009 (Вс) 0:10

А что без трансляции не как? Рисуй только видимую часть, на отдельном битмапе, а в Paint выводи этот битмап на контрол....
"Им будет не просто, тем кто полагается на истину авторитета, вместо того чтобы полагаться на авторитет Истины"
Джеральд Месси, Египтолог

MIT
Мега гуру
Мега гуру
Аватара пользователя
 
Сообщения: 2211
Зарегистрирован: 17.09.2006 (Вс) 22:46

Re: Работа с GDI+. Проблема: Оптимизация

Сообщение MIT » 16.02.2009 (Пн) 17:14

Scarabey писал(а):А что без трансляции не как? Рисуй только видимую часть, на отдельном битмапе, а в Paint выводи этот битмап на контрол....
Это более медленная версия первого способа. Была изначально, затем модифицирована в вышесказанное.
Nord777 писал(а):Создавай процедуру тестирования и замеряй время.
Мысленный эксеримент показал, что процент сложности воссоздания 2ого способа выше потенциально возможной от него пользы, соответственно признался мной нерентабельным :)
Изображение
You can change your face, but can`t change your mind. No matter what you do.
Создайте еще более понятный интерфейс и мир создаст еще более тупого юзера. (с) Баш

MIT
Мега гуру
Мега гуру
Аватара пользователя
 
Сообщения: 2211
Зарегистрирован: 17.09.2006 (Вс) 22:46

Re: Работа с GDI+. Проблема: Gaussian Blur

Сообщение MIT » 17.02.2009 (Вт) 23:41

Обсуждения подтемы "Gaussian Blur"

Собственно вот. Необходим вменяемый человекопонятный, работающий с двухмерным массивом GBlur-алгоритм.
Пример товарища Mikle`а меня вполне устраивает(точнее некотороя его модификация относительно альфы), за исключением того, что работает он с одномерным массивом, а мне нужна работа обязательно с духмерным массивом. Сам переделать не смог ( :oops: , понимаю, что фигня, но вот не получается и фсё тут, прям аж бесит), так что если кто-то поможет буду категорически благодарен.
Вообще по сети разбросано довольно много вариаций Blur-эффекта (стоит особо отметить хороший пример наших восточных коллег), но они мне, если честно не нравятся (в упомянутом выше слишком умная система работы с альфой, мне так не нужно, но как вариант, пусть и неоптимизированный, сойдет). Также следует упомянуть вот это.

Кто чем может помочь?
Изображение
You can change your face, but can`t change your mind. No matter what you do.
Создайте еще более понятный интерфейс и мир создаст еще более тупого юзера. (с) Баш

kitsemen
Начинающий
Начинающий
Аватара пользователя
 
Сообщения: 5
Зарегистрирован: 06.02.2009 (Пт) 6:37

Re: Работа с GDI+. Проблема: Gaussian Blur

Сообщение kitsemen » 30.03.2009 (Пн) 4:49

С двухмерным не обязательно, можно с одноменым, реализовав функии доступа к данным GetPixel и SetPixel например так:

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

Public Strucrure Argb

   Public b As Byte
   Public g As Byte
   Public r As Byte
   Public a As Byte

   Public Sub New(ByRef a As Byte, ByRef r As Byte, ByRef g As Byte, ByRef b As Byte)
      me.a = a: me.r = r: me.g = g: me.b = b
   End Sub

End Structure

Public Class Image2D

Private data() As Byte
Private width As Int32
Private height As Int32

Public Sub New(ByVal width As Int32, ByVal height As Int32)
   Me.width = width: me.height = height
   ReDim data((width * height * 4) - 1)
End Sub

Public Sub New(ByVal source As Bitmap)
   MyBase.New()
   Me.width = source.width: me.height = source.height
   ReDim data((width * height * 4) - 1)
   Using bm As New Bitmap(source.Width, source.Height, PixelFormat.Format32bppArgb) 
      Call bm.SetResolution(source.HorizontalResolution, source.VerticalResolution)
      Using g As Graphics = Graphics.FromImage(bmp)
         Call g.DrawImage(source, 0, 0) 
      End Using
      Dim bmdata As BitmapData = bm.LockBits(New Rectangle(0, _ 
      0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb) 
      Call Marshal.Copy(bmdata.Scan0, data, 0, data.Length) 
      Call bm.UnlockBits(bmdata) 
   End Using
End Sub

Public Sub SetPixel(ByRef x As Int32, ByRef y As Int32, ByRef value As Argb)
   On Error Resume Next
   Dim ofs As Integer = ((y * width) + x) * 4
   data(ofs) = value.b
   data(ofs + 1) = value.g
   data(ofs + 2) = value.r
   data(ofs + 3) = value.a
End Sub

Public Function GetPixel(ByRef x As Int32, ByRef y As Int32) As Argb
   On Error Resume Next
   Dim ofs As Integer = ((y * width) + x) * 4
   Return New Argb(data(ofs + 3), data(ofs + 2), data(ofs + 1), data(ofs))
End Function

Public Function ToBitmap() As Bitmap
   Dim bm As New Bitmap(width, height, PixelFormat.Format32bppArgb) 
   Dim bmdata As BitmapData = bm.LockBits(New Rectangle(0, 0, _ 
   width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb) 
   Call Marshal.Copy(data, 0, bmdata.Scan0, data.Length) 
   Call bmp.UnlockBits(bmdata)
   Return bm
End Function

End Class


Байтовый массив в 2-3 раза быстрее за Marshal.ReadByte и Marshal.WriteByte! Потому именно так реализовано.

А суть "Смягчения" либо "Обострения" в следующем состоит. Читаем на каждом пикселе и суммируем значения восьми окружающих пикселей. Получаем сумму для каждого канала; делим на 8 (средний показатель).

Например нужно смягчать либо обострять согласно указанному в процентах значению, условно "сила_применения"

При "Смягчении":

Работаем начиная не нулевым а с первого пикселя по ширине и высоте и до (ширина - 2) и (высота - 2) соответственно. Обрабатывать поколоночно?, полинейно? - нет разницы!

Код: Выделить всё
значение = Image2D.GetPixel(x,y)

If значение_канала > средний_показатель Then
   новое_значение += ((средний_показатель - значение_канала) / 100) * сила_применения
Else
   новое_значение -= ((значение_канала - средний_показатель) / 100) * сила_применения
End If

Call OtherImage2D.SetPixel(x,y, новое_значение) ' сохраняем результаты работы в новом объекте Image2D


То есть суть "смягчения" состоит в приближении значения к окружению. При "Обострении" - наоборот.

Можно gaussian смягчение и обострение реализовать похожим образом работая с т.н. свёрточной матрицей. Пример можно взять у меня на сайте, хотя то и не Blur и не Sharpen, а Emboss. http://igorr.110mb.com/doc_nonlinearedit.html
Ваш Семен

MIT
Мега гуру
Мега гуру
Аватара пользователя
 
Сообщения: 2211
Зарегистрирован: 17.09.2006 (Вс) 22:46

Re: Работа с GDI+. Проблема: Gaussian Blur

Сообщение MIT » 30.03.2009 (Пн) 7:13

kitsemen писал(а):С двухмерным не обязательно, можно с одноменым, реализовав функии доступа к данным GetPixel и SetPixel например так:
Хм... Вообще я уже и забыл про эту тему... Ну да ладно, спасибо что ответил :)
kitsemen писал(а):Байтовый массив в 2-3 раза быстрее за Marshal.ReadByte и Marshal.WriteByte! Потому именно так реализовано.
Если говорить о производительности, то твоя методика доступа к данным не является рациональной: эксперементально доказано, что умножение - очень медленная операция (в масштабах сравнения со сложением и, возможно, битовыми сдвигами), да и сам факт того, что будут произаедено огромное количество вызовов немного настораживает - намного правильнее реализовать все в одной функции/процедуре :)

kitsemen писал(а):А суть "Смягчения" либо "Обострения" в следующем состоит. Читаем на каждом пикселе и суммируем значения восьми окружающих пикселей. Получаем сумму для каждого канала; делим на 8 (средний показатель).Например нужно смягчать либо обострять согласно указанному в процентах значению, условно "сила_применения"При "Смягчении":Работаем начиная не нулевым а с первого пикселя по ширине и высоте и до (ширина - 2) и (высота - 2) соответственно. Обрабатывать поколоночно?, полинейно? - нет разницы!
Непонял. А как реализовать blur одним проходом? Я где-то читал, что сама технология Гаусса подразумевает вертикальную и горизонтальную фазу обработки (кстати, с помощью неполной обработки можно реализовать "размытие в движении").

kitsemen писал(а):Можно gaussian смягчение и обострение реализовать похожим образом работая с т.н. свёрточной матрицей. Пример можно взять у меня на сайте, хотя то и не Blur и не Sharpen, а Emboss. http://igorr.110mb.com/doc_nonlinearedit.html
За это реально спасибо, почитаю :)
Изображение
You can change your face, but can`t change your mind. No matter what you do.
Создайте еще более понятный интерфейс и мир создаст еще более тупого юзера. (с) Баш

kitsemen
Начинающий
Начинающий
Аватара пользователя
 
Сообщения: 5
Зарегистрирован: 06.02.2009 (Пт) 6:37

Re: Работа с GDI+. Проблема: Gaussian Blur

Сообщение kitsemen » 31.03.2009 (Вт) 3:37

MIT писал(а):намного правильнее реализовать все в одной функции/процедуре :)


Средствами Marshal.Copy я не смог скопировать одномерный байтовый массив, преобразовав в двумерный структур типа Argb (4 байта). Не вышло даже при помощи старой доброй MoveMemory. Я старался :D. Решил потому сделать именно так.

MIT писал(а):Непонял. А как реализовать blur одним проходом? Я где-то читал, что сама технология Гаусса подразумевает вертикальную и горизонтальную фазу обработки (кстати, с помощью неполной обработки можно реализовать "размытие в движении").


Вообще правильно. Именно GaussianBlur в один проход нельзя. В приаттачменте он реализован в 4 прохода на vb2008 (по диагоналям тудом сюдом обрабатывается). Хотя можо попробовать оним, случайно выбирая точки окружения (предыдущую и следующую) для каждого пиксела.

А вообще Blur (размытие в смысле) можно реализовать по разному. Например MedianBlur. Там тоже есть. Либо путем сверточной матрицы, как я в предыдущем посте писал. Тоже там есть.
Вложения
Filters.zip
Blur (Gaussian, Median, путем сверточной матрицы)
(2.64 Кб) Скачиваний: 105
Ваш Семен

kitsemen
Начинающий
Начинающий
Аватара пользователя
 
Сообщения: 5
Зарегистрирован: 06.02.2009 (Пт) 6:37

Re: Работа с GDI+. Проблема: Gaussian Blur

Сообщение kitsemen » 01.04.2009 (Ср) 0:42

kitsemen писал(а):Вообще правильно. Именно GaussianBlur в один проход нельзя. В приаттачменте он реализован в 4 прохода на vb2008 (по диагоналям тудом сюдом обрабатывается). Хотя можо попробовать оним, случайно выбирая точки окружения (предыдущую и следующую) для каждого пиксела.


PS. Можно например так одним обходом сделать Blur (примерно так точки для смягчения выбирать):

Код: Выделить всё
#Region " Jitter"

Public Class Jitter

Implements IFilter

Private compositing As Single

' compositing 0.0 - 1.0
Public Sub New(Optional ByVal compositing As Single = 0.5)
   MyBase.New()
   If (compositing < 0.01) OrElse (compositing > 1) Then compositing = 0.5
   Me.compositing = compositing
End Sub

Private points() As Point = New Point() {New Point(-1, -1), New Point(-1, 0), New Point(-1, 1), _
                                         New Point(0, -1), New Point(0, 0), New Point(0, 1), _
                                         New Point(1, -1), New Point(1, 0), New Point(1, 1)}

Public Sub ApplyTo(ByRef i2d As Image2D) Implements IFilter.ApplyTo
   Dim w, h As Int32, pcur, pnext As Argb
   w = i2d.width - 1 : h = i2d.height - 1
   Dim r As New Random(), n, nn, rx1, ry1, rx2, ry2 As Int32
   For y As Int32 = 1 To h - 1 Step 1
       For x As Int32 = 1 To w - 1 Step 1
           n = r.Next(0, 8)
           rx1 = x + points(n).X
           ry1 = y + points(n).Y
           pcur = i2d.GetPixel(rx1, ry1)
           While nn = n : nn = r.Next(0, 8) : End While
           rx2 = x + points(nn).X
           ry2 = y + points(nn).Y
           pnext = i2d.GetPixel(rx2, ry2)
           pcur = Argb.OverlayRgb(pnext, pcur, compositing)
           pnext = Argb.OverlayRgb(pcur, pnext, compositing)
           Call i2d.SetPixel(rx2, ry2, pcur)
           Call i2d.SetPixel(rx1, ry1, pnext)
       Next
   Next
End Sub

End Class

#End Region


Это дописать нужно ко вложенному в предыдущем посте файлу.
Ваш Семен

Пред.

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

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

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

    TopList