Re[3]: BitmapSource - работа с пикселями
От: samius Япония http://sams-tricks.blogspot.com
Дата: 22.12.09 13:49
Оценка: 13 (2)
Здравствуйте, KW_, Вы писали:

KW_>Остаётся тупая обезьянья работа по реализации ручками кучи форматов. Действительно при чём тут WPF, когда есть старое, доброе, шустрое и понятное WIN API.


Не компрометируйте Седой Урал. Юзайте FormatConvertedBitmap + WritableBitmap.
Re: BitmapSource - работа с пикселями
От: KW_ Россия  
Дата: 25.12.09 09:48
Оценка: 6 (2)
Всем СПС за подсказки. Вот написал 2 класса для облегчения жизни.
Первый класс расширяет функционал BitmapSource и Bitmap удобными методами для конвертаций друг в друга и в разные форматы пикселей.
Второй класс предназначен для быстрой попиксельной обработки изображений BitmapSource и Bitmap с рядом дополнительных возможностей.
Если кому надо пользуйтесь

    /// <summary>
    /// Расширение функционала битмапов методами конвертаций.
    /// </summary>
    public static class BitmapConvertedKW
    {
        /// <summary>
        /// Конвертация BitmapSource в другой формат BitmapSource.
        /// </summary>
        /// <param name="source">Источник для конвертации.</param>
        /// <param name="destinationFormat">Новый формат.</param>
        /// <param name="destinationPalette">
        /// Палитра для нового формата, если конечно она нужна для нового формата, иначе передать null.
        /// </param>
        /// <returns>BitmapSource в новом формате.</returns>
        public static BitmapSource ConvertTo(
            this BitmapSource source,
            PixelFormat destinationFormat,
            BitmapPalette destinationPalette)
        {
            return new FormatConvertedBitmap(
                source, destinationFormat, destinationPalette, 0);
        }

        /// <summary>
        /// Конвертация Bitmap в другой формат Bitmap.
        /// </summary>
        /// <param name="source">Источник для конвертации.</param>
        /// <param name="destinationFormat">Новый формат.</param>
        /// <param name="destinationPalette">
        /// Палитра для нового формата, если конечно она нужна для нового формата, иначе передать null.
        /// </param>
        /// <returns>Bitmap в новом формате.</returns>
        public static System.Drawing.Bitmap ConvertTo(
            this System.Drawing.Bitmap source,
            System.Drawing.Imaging.PixelFormat destinationFormat,
            System.Drawing.Imaging.ColorPalette destinationPalette)
        {
            System.Drawing.Bitmap result =
                new System.Drawing.Bitmap(source.Width, source.Height, destinationFormat);
            if (destinationPalette != null)
                result.Palette = destinationPalette;
            using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(result))
                g.DrawImage(source, 0, 0);
            return result;
        }

        /// <summary>
        /// Конвертация Bitmap в BitmapSource.
        /// </summary>
        public static BitmapSource ConvertTo(this System.Drawing.Bitmap source)
        {
            return (new BitmapWrapperKW(source)).ToBitmapSource();
        }

        /// <summary>
        /// Конвертация BitmapSource в Bitmap.
        /// </summary>
        public static System.Drawing.Bitmap ConvertTo(this BitmapSource source)
        {
            return (new BitmapWrapperKW(source)).ToBitmap();
        }
    }

    /// <summary>
    /// Класс позволяет производить шустро попиксельные операции с битмапами.
    /// При этом результат операций возвращается в виде нового битмапа.
    /// </summary>
    [Serializable]
    public class BitmapWrapperKW
    {
        #region Внутренние поля и методы.

        double _dpiX;
        double _dpiY;
        int _height;
        int _width;
        byte[] _pixels;

        private BitmapSource ToGrayscaleTransparentBitmapSource()
        {
            byte[] pixelsGray = new byte[_width * _height * 4];
            int offset = 0;
            int offsetGray = 0;
            int cnt = _height * _width;
            for (int i = 0; i < cnt; i++)
            {
                byte blue = _pixels[offset++];
                byte green = _pixels[offset++];
                byte red = _pixels[offset++];
                byte alfa = _pixels[offset++];

                byte gray = (byte)(0.299 * red + 0.587 * green + 0.114 * blue);

                pixelsGray[offsetGray++] = gray;
                pixelsGray[offsetGray++] = gray;
                pixelsGray[offsetGray++] = gray;
                pixelsGray[offsetGray++] = alfa;
            }

            return BitmapSource.Create(
                _width, _height, _dpiX, _dpiY, PixelFormats.Bgra32, null, pixelsGray, _width * 4);
        }

        private System.Drawing.Bitmap ToTransparentBitmap()
        {
            System.Drawing.Bitmap bm = new System.Drawing.Bitmap(
                _width, _height,
                System.Drawing.Imaging.PixelFormat.Format32bppArgb
                );

            System.Drawing.Imaging.BitmapData bmData = bm.LockBits(
                new System.Drawing.Rectangle(0, 0, _width, _height),
                System.Drawing.Imaging.ImageLockMode.ReadWrite,
                System.Drawing.Imaging.PixelFormat.Format32bppArgb
                );

            int strideBM = bmData.Stride;
            System.IntPtr Scan0 = bmData.Scan0;

            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                int offset = 0;
                int cnt = _height * _width;
                for (int i = 0; i < cnt; i++)
                {
                    p[offset++] = _pixels[offset];//blue
                    p[offset++] = _pixels[offset];//green
                    p[offset++] = _pixels[offset];//red
                    p[offset++] = _pixels[offset];//alfa
                }
            }

            bm.UnlockBits(bmData);

            return bm;
        }

        #endregion

        /// <summary>
        /// Конструктор копирует пиксели из заданного битмапа.
        /// </summary>
        /// <param name="source">Исходный битмап.</param>
        public BitmapWrapperKW(BitmapSource source)
        {
            _dpiX = source.DpiX;
            _dpiY = source.DpiY;
            _height = source.PixelHeight;
            _width = source.PixelWidth;

            if (source.Format != PixelFormats.Bgra32)
                source = source.ConvertTo(PixelFormats.Bgra32, null);

            int stride = _width * 4;
            _pixels = new byte[stride * _height];
            source.CopyPixels(_pixels, stride, 0);
        }

        /// <summary>
        /// Конструктор копирует пиксели из заданного битмапа.
        /// </summary>
        /// <param name="source">Исходный битмап.</param>
        public BitmapWrapperKW(System.Drawing.Bitmap source)
        {
            _dpiX = source.HorizontalResolution;
            _dpiY = source.VerticalResolution;
            _height = source.Height;
            _width = source.Width;

            System.Drawing.Imaging.PixelFormat pf = source.PixelFormat;

            if (pf != System.Drawing.Imaging.PixelFormat.Format32bppArgb)
                source = source.ConvertTo(System.Drawing.Imaging.PixelFormat.Format32bppArgb, null);

            System.Windows.Media.PixelFormat _pf = System.Windows.Media.PixelFormats.Bgra32;
            _pixels = new byte[_width * _height * 4];

            System.Drawing.Imaging.BitmapData bmData = source.LockBits(
                new System.Drawing.Rectangle(0, 0, _width, _height),
                System.Drawing.Imaging.ImageLockMode.ReadWrite,
                System.Drawing.Imaging.PixelFormat.Format32bppArgb
                );

            int strideBM = bmData.Stride;
            System.IntPtr Scan0 = bmData.Scan0;

            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                int offset = 0;
                for (int j = 0; j < _height; j++)
                {
                    int offsetBM = strideBM * j;
                    for (int i = 0; i < _width; i++)
                    {
                        _pixels[offset++] = p[offsetBM++];//blue
                        _pixels[offset++] = p[offsetBM++];//green
                        _pixels[offset++] = p[offsetBM++];//red
                        _pixels[offset++] = p[offsetBM++];//alfa
                    }
                }
            }

            source.UnlockBits(bmData);
        }

        /// <summary>
        /// Конструктор загружает битмап из файла типа:
        /// BMP, GIF, EXIG, JPG, PNG and TIFF.
        /// </summary>
        /// <param name="fileName">Полное имя файла.</param>
        public BitmapWrapperKW(string fileName)
            : this(new System.Drawing.Bitmap(fileName))
        { }

        /// <summary>
        /// Ширина в пикселях.
        /// </summary>
        public int PixelWidth
        {
            get { return _width; }
        }

        /// <summary>
        /// Высота в пикселях.
        /// </summary>
        public int PixelHeight
        {
            get { return _height; }
        }

        /// <summary>
        /// Прямой доступ к пикселям в формате sRGB 32 бита на пиксел.
        /// Каждый канал по 8 бит. Порядок следования: blue, green, red, alpha.
        /// </summary>
        /// <example>
        /// int offset = (y * PixelWidth + x) * 4;
        /// byte blue = Pixels[offset++]
        /// byte green = Pixels[offset++]
        /// byte red = Pixels[offset++]
        /// byte alfa = Pixels[offset]
        /// </example>
        public byte[] Pixels
        {
            get { return _pixels; }
        }

        /// <summary>
        /// Пиксели
        /// </summary>
        /// <param name="x">Номер колонки пикселей</param>
        /// <param name="y">Номер строки пикселей</param>
        /// <returns>Цвет пикселя.</returns>
        public System.Windows.Media.Color this[int x, int y]
        {
            get
            {
                int offset = (y * _width + x) * 4;
                byte blue = _pixels[offset++];
                byte green = _pixels[offset++];
                byte red = _pixels[offset++];
                byte alfa = _pixels[offset];
                return System.Windows.Media.Color.FromArgb(alfa, red, green, blue);
            }

            set
            {
                int offset = (y * _width + x) * 4;

                _pixels[offset++] = value.B;
                _pixels[offset++] = value.G;
                _pixels[offset++] = value.R;
                _pixels[offset] = value.A;
            }
        }

        public BitmapSource ToBitmapSource()
        {
            return System.Windows.Media.Imaging.BitmapSource.Create(
                _width, _height, _dpiX, _dpiY, System.Windows.Media.PixelFormats.Bgra32, null, _pixels, _width * 4);
        }

        /// <summary>
        /// Получить копию изображения в оттенках серого цвета.
        /// </summary>
        public BitmapSource ToGrayscaleBitmapSource()
        {
            // Проверка на прозрачность, если есть прозрачные пиксели то обработать особым образом.
            int offset = 3;
            int cnt = _width * _height;
            for (int i = 0; i < cnt; i++)
            {
                if (_pixels[offset] != 255)
                    return ToGrayscaleTransparentBitmapSource();
                offset += 4;
            }

            ushort[] pixelsGray = new ushort[_width * _height];
            offset = 0;
            int offsetGray = 0;
            for (int i = 0; i < cnt; i++)
            {
                byte blue = _pixels[offset++];
                byte green = _pixels[offset++];
                byte red = _pixels[offset];
                offset += 2;
                ushort gray = (ushort)((65535 * 0.299 / 255) * red + (65535 * 0.587 / 255) * green + (65535 * 0.114 / 255) * blue);
                pixelsGray[offsetGray++] = gray;
            }

            return BitmapSource.Create(
                _width, _height, _dpiX, _dpiY, PixelFormats.Gray16, null, pixelsGray, 2 * _width);
        }

        public System.Drawing.Bitmap ToBitmap()
        {
            // Проверка на прозрачность, если есть прозрачные пиксели то обработать особым образом.
            int offset = 3;
            int cnt = _width * _height;
            for (int i = 0; i < cnt; i++)
            {
                if (_pixels[offset] != 255)
                    return ToTransparentBitmap();
                offset += 4;
            }

            System.Drawing.Bitmap bm = new System.Drawing.Bitmap(
                _width, _height,
                System.Drawing.Imaging.PixelFormat.Format24bppRgb
                );

            System.Drawing.Imaging.BitmapData bmData = bm.LockBits(
                new System.Drawing.Rectangle(0, 0, _width, _height),
                System.Drawing.Imaging.ImageLockMode.ReadWrite,
                System.Drawing.Imaging.PixelFormat.Format24bppRgb
                );

            int strideBM = bmData.Stride;
            System.IntPtr Scan0 = bmData.Scan0;

            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                offset = 0;
                for (int j = 0; j < _height; j++)
                {
                    int offsetBM = strideBM * j;
                    for (int i = 0; i < _width; i++)
                    {
                        p[offsetBM++] = _pixels[offset++];//blue
                        p[offsetBM++] = _pixels[offset++];//green
                        p[offsetBM++] = _pixels[offset];//red
                        offset += 2;
                    }
                }
            }

            bm.UnlockBits(bmData);

            return bm;
        }

        public void SaveToFileBMP(string fileName)
        {
            ToBitmap().Save(fileName);
        }
    }
Re: BitmapSource - работа с пикселями
От: TATAPuH США  
Дата: 22.12.09 07:15
Оценка: 12 (1)
Здравствуйте, 8086, Вы писали:

8>Как я понимаю, BitmapSource это "правильный" класс битмапов для использования в WPF. Неожиданно обнаружил, что BitmapSource не дает непосредственного доступа к пикселям, как старый Bitmap с его Set/GetPixel. Какой существует метод манипулирования битмапами в WPF. Может стоит взять вместо BitmapSource, что то другое?


я так понимаю вам сюда
http://khason.net/blog/how-to-high-performance-graphics-in-wpf/
ну или сюда
http://langexplr.blogspot.com/2007/12/creating-fractal-images-with-c-30.html

"Всего-то и нужно — взять изображение, поменять в нем несколько пикселов и записать его обратно. Судя по всему с BitmapSource/WriteableBitmap я иду в неверном направлении"
а вообще проще воспользоваться стандартной битмапой если дело всеголиш в паре пикселей

при чём тут именно впф не понятно
Re[7]: BitmapSource - работа с пикселями
От: samius Япония http://sams-tricks.blogspot.com
Дата: 22.12.09 14:33
Оценка: +1
Здравствуйте, 8086, Вы писали:

8>Проблема не в арифметике. Но не хочется делать лишних предположений относительно внутреннего устройства битмапа. Например — количество байт на пиксель.

Кол-во байт на пискель зависит от указанного формата точно так же как и в старом добром Bitmap-е.

8>Короче говоря, хоть и уверен, что такой подход абсолютно неверен и кроссректален, решил конвертировать BitmapSource в старый Bitmap делать все манипуляции с ним и в конце конвертировать из Bitmap в BitmapSource.


Ну это тоже вариант, особенно если нужна сила GDI при редактировании битмапа. Ничего кроссректального не вижу.
BitmapSource - работа с пикселями
От: 8086  
Дата: 18.12.09 14:31
Оценка:
Как я понимаю, BitmapSource это "правильный" класс битмапов для использования в WPF. Неожиданно обнаружил, что BitmapSource не дает непосредственного доступа к пикселям, как старый Bitmap с его Set/GetPixel. Какой существует метод манипулирования битмапами в WPF. Может стоит взять вместо BitmapSource, что то другое?
Re: BitmapSource - работа с пикселями
От: Codechanger Россия  
Дата: 18.12.09 14:52
Оценка:
Здравствуйте, 8086, Вы писали:

8>Как я понимаю, BitmapSource это "правильный" класс битмапов для использования в WPF. Неожиданно обнаружил, что BitmapSource не дает непосредственного доступа к пикселям, как старый Bitmap с его Set/GetPixel. Какой существует метод манипулирования битмапами в WPF. Может стоит взять вместо BitmapSource, что то другое?


http://msdn.microsoft.com/ru-ru/library/system.windows.media.imaging.writeablebitmap.aspx
Re[2]: BitmapSource - работа с пикселями
От: 8086  
Дата: 18.12.09 16:45
Оценка:
Но в WritableBitmap нет методов для доступа к конкретному пикселю (?)

C>http://msdn.microsoft.com/ru-ru/library/system.windows.media.imaging.writeablebitmap.aspx
Re[3]: BitmapSource - работа с пикселями
От: Codechanger Россия  
Дата: 19.12.09 09:27
Оценка:
Здравствуйте, 8086, Вы писали:

8>Но в WritableBitmap нет методов для доступа к конкретному пикселю (?)


C>>http://msdn.microsoft.com/ru-ru/library/system.windows.media.imaging.writeablebitmap.aspx


Есть, но на более низком уровне. Работа там происходит напрямую с массивом. Т.е. если вы хотите методы доступа к отдельному пикселу, вам придется их написать отдельно.
Re[4]: BitmapSource - работа с пикселями
От: 8086  
Дата: 20.12.09 12:41
Оценка:
А есть ли более безгеморойный способ работы с битмапом? Всего-то и нужно — взять изображение, поменять в нем несколько пикселов и записать его обратно. Судя по всему с BitmapSource/WriteableBitmap я иду в неверном направлении.

C>Здравствуйте, 8086, Вы писали:


8>>Но в WritableBitmap нет методов для доступа к конкретному пикселю (?)


C>>>http://msdn.microsoft.com/ru-ru/library/system.windows.media.imaging.writeablebitmap.aspx


C>Есть, но на более низком уровне. Работа там происходит напрямую с массивом. Т.е. если вы хотите методы доступа к отдельному пикселу, вам придется их написать отдельно.
Re[5]: BitmapSource - работа с пикселями
От: Codechanger Россия  
Дата: 21.12.09 06:36
Оценка:
Здравствуйте, 8086, Вы писали:

8>А есть ли более безгеморойный способ работы с битмапом? Всего-то и нужно — взять изображение, поменять в нем несколько пикселов и записать его обратно. Судя по всему с BitmapSource/WriteableBitmap я иду в неверном направлении.


C>>Здравствуйте, 8086, Вы писали:


8>>>Но в WritableBitmap нет методов для доступа к конкретному пикселю (?)


C>>>>http://msdn.microsoft.com/ru-ru/library/system.windows.media.imaging.writeablebitmap.aspx


C>>Есть, но на более низком уровне. Работа там происходит напрямую с массивом. Т.е. если вы хотите методы доступа к отдельному пикселу, вам придется их написать отдельно.


Для WPF вроде как не наблюдается. Поройте гугль, возможно, кто-нить уже обертку написал
Re[2]: BitmapSource - работа с пикселями
От: KW_ Россия  
Дата: 22.12.09 12:40
Оценка:
Здравствуйте, TATAPuH, Вы писали:

TAT>при чём тут именно впф не понятно


Очень даже понятно. У человека есть BitmapSource и ему его надо обработать. При этом у него наверно нет выбора, так как ему приходит BitmapSource

Проблема в том что форматов пикселей в BitmapSource больше 20 (см. PixelFormats), более того форматы эти не просто какое-то конечное перечисление, это класс, а значит потенциально форматов бесконечное множество. Другими словами форматов вообще нет ни каких, так как большое количество форматов равно отсутствию таковых, ну не будет же человек ручками реализовывать бесконечность.
Возможные выходы:
1) Универсальное преобразования из одного формата в другой. Тогда можно ручками реализовать работу с Bgra32 а все другие форматы туда перегонять. Лично я не нашёл. Если кто знает подскажите ПЛИЗ!.
2) Сохранить бы его в файл BMP, а потом грузануть его в «человеческий» класс для обработки потом обратно сохранить/загрузить в исходный BitmapSource. Опять же я лично ничего такого не нарыл.
3) Создать BitmapSource формата Bgra32 и отрисовать на нём исходный BitmapSource. Отредактировать новый BitmapSource и перерисовать обратно. Тут тоже я ничего не нарыл.

Остаётся тупая обезьянья работа по реализации ручками кучи форматов. Действительно при чём тут WPF, когда есть старое, доброе, шустрое и понятное WIN API.
Re[4]: BitmapSource - работа с пикселями
От: 8086  
Дата: 22.12.09 14:06
Оценка:
Но WritableBitmap тазже не дает прямого доступа к пикселю по его координатам.

S>Не компрометируйте Седой Урал. Юзайте FormatConvertedBitmap + WritableBitmap.
Re[5]: BitmapSource - работа с пикселями
От: samius Япония http://sams-tricks.blogspot.com
Дата: 22.12.09 14:22
Оценка:
Здравствуйте, 8086, Вы писали:

8>Но WritableBitmap тазже не дает прямого доступа к пикселю по его координатам.


Но дает прямой доступ ко всему буферу. Чтобы получить доступ к пикселю по координатам нужно применить арифметику уровня 2-го класса.
Re[6]: BitmapSource - работа с пикселями
От: 8086  
Дата: 22.12.09 14:30
Оценка:
Проблема не в арифметике. Но не хочется делать лишних предположений относительно внутреннего устройства битмапа. Например — количество байт на пиксель. Короче говоря, хоть и уверен, что такой подход абсолютно неверен и кроссректален, решил конвертировать BitmapSource в старый Bitmap делать все манипуляции с ним и в конце конвертировать из Bitmap в BitmapSource.

S>Здравствуйте, 8086, Вы писали:


8>>Но WritableBitmap тазже не дает прямого доступа к пикселю по его координатам.


S>Но дает прямой доступ ко всему буферу. Чтобы получить доступ к пикселю по координатам нужно применить арифметику уровня 2-го класса.
Re[3]: BitmapSource - работа с пикселями
От: Silver_s Ниоткуда  
Дата: 22.12.09 19:45
Оценка:
Здравствуйте, KW_, Вы писали:

KW_>Возможные выходы:

KW_>1) Универсальное преобразования из одного формата в другой. Тогда можно ручками реализовать работу с Bgra32 а все другие форматы туда перегонять. Лично я не нашёл. Если кто знает подскажите ПЛИЗ!.
KW_>2) Сохранить бы его в файл BMP, а потом грузануть его в «человеческий» класс для обработки потом обратно сохранить/загрузить в исходный BitmapSource. Опять же я лично ничего такого не нарыл.
KW_>3) Создать BitmapSource формата Bgra32 и отрисовать на нём исходный BitmapSource. Отредактировать новый BitmapSource и перерисовать обратно. Тут тоже я ничего не нарыл.

На худой конец можно WIC попробовать. Managed обертки есть. Правда не знаю будет ли работать на чем-то кроме Windows 7.

Что-то типа такого:

BitmapDecoder bitmapDecoder=factory.CreateDecoderFromStream(stream, DecodeMetadataCacheOptions.OnDemand);
BitmapSource bitmapSource = bitmapDecoder.GetFrame(0).ToBitmapSource();

WICFormatConverter converter = factory.CreateFormatConverter();
converter.Initialize(bitmapSource, PixelFormats.Pf32bppBGRA, BitmapDitherType.None, BitmapPaletteType.Custom);
 //use:
IntPtr bufferPtr = converter.CopyPixelsToMemory();
byte[] ar = converter.CopyPixels();
BitmapSource bs = converter.ToBitmapSource();
Re: BitmapSource - работа с пикселями
От: HotDog Швейцария www.denebspace.com
Дата: 23.12.09 14:13
Оценка:
Здравствуйте, 8086, Вы писали:

8>Как я понимаю, BitmapSource это "правильный" класс битмапов для использования в WPF. Неожиданно обнаружил, что BitmapSource не дает непосредственного доступа к пикселям, как старый Bitmap с его Set/GetPixel. Какой существует метод манипулирования битмапами в WPF. Может стоит взять вместо BitmapSource, что то другое?


http://kodierer.blogspot.com/2009/12/writeablebitmapex-writeablebitmap.html

...WriteableBitmap Extension Methods introduced the SetPixel methods.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.