Как я понимаю, BitmapSource это "правильный" класс битмапов для использования в WPF. Неожиданно обнаружил, что BitmapSource не дает непосредственного доступа к пикселям, как старый Bitmap с его Set/GetPixel. Какой существует метод манипулирования битмапами в WPF. Может стоит взять вместо BitmapSource, что то другое?
Здравствуйте, 8086, Вы писали:
8>Как я понимаю, BitmapSource это "правильный" класс битмапов для использования в WPF. Неожиданно обнаружил, что BitmapSource не дает непосредственного доступа к пикселям, как старый Bitmap с его Set/GetPixel. Какой существует метод манипулирования битмапами в WPF. Может стоит взять вместо BitmapSource, что то другое?
Есть, но на более низком уровне. Работа там происходит напрямую с массивом. Т.е. если вы хотите методы доступа к отдельному пикселу, вам придется их написать отдельно.
А есть ли более безгеморойный способ работы с битмапом? Всего-то и нужно — взять изображение, поменять в нем несколько пикселов и записать его обратно. Судя по всему с BitmapSource/WriteableBitmap я иду в неверном направлении.
C>Здравствуйте, 8086, Вы писали:
8>>Но в WritableBitmap нет методов для доступа к конкретному пикселю (?)
C>>>http://msdn.microsoft.com/ru-ru/library/system.windows.media.imaging.writeablebitmap.aspx
C>Есть, но на более низком уровне. Работа там происходит напрямую с массивом. Т.е. если вы хотите методы доступа к отдельному пикселу, вам придется их написать отдельно.
Здравствуйте, 8086, Вы писали:
8>А есть ли более безгеморойный способ работы с битмапом? Всего-то и нужно — взять изображение, поменять в нем несколько пикселов и записать его обратно. Судя по всему с BitmapSource/WriteableBitmap я иду в неверном направлении.
C>>Здравствуйте, 8086, Вы писали:
8>>>Но в WritableBitmap нет методов для доступа к конкретному пикселю (?)
C>>>>http://msdn.microsoft.com/ru-ru/library/system.windows.media.imaging.writeablebitmap.aspx
C>>Есть, но на более низком уровне. Работа там происходит напрямую с массивом. Т.е. если вы хотите методы доступа к отдельному пикселу, вам придется их написать отдельно.
Для WPF вроде как не наблюдается. Поройте гугль, возможно, кто-нить уже обертку написал
Здравствуйте, 8086, Вы писали:
8>Как я понимаю, BitmapSource это "правильный" класс битмапов для использования в WPF. Неожиданно обнаружил, что BitmapSource не дает непосредственного доступа к пикселям, как старый Bitmap с его Set/GetPixel. Какой существует метод манипулирования битмапами в WPF. Может стоит взять вместо BitmapSource, что то другое?
"Всего-то и нужно — взять изображение, поменять в нем несколько пикселов и записать его обратно. Судя по всему с BitmapSource/WriteableBitmap я иду в неверном направлении"
а вообще проще воспользоваться стандартной битмапой если дело всеголиш в паре пикселей
Здравствуйте, TATAPuH, Вы писали:
TAT>при чём тут именно впф не понятно
Очень даже понятно. У человека есть BitmapSource и ему его надо обработать. При этом у него наверно нет выбора, так как ему приходит BitmapSource
Проблема в том что форматов пикселей в BitmapSource больше 20 (см. PixelFormats), более того форматы эти не просто какое-то конечное перечисление, это класс, а значит потенциально форматов бесконечное множество. Другими словами форматов вообще нет ни каких, так как большое количество форматов равно отсутствию таковых, ну не будет же человек ручками реализовывать бесконечность.
Возможные выходы:
1) Универсальное преобразования из одного формата в другой. Тогда можно ручками реализовать работу с Bgra32 а все другие форматы туда перегонять. Лично я не нашёл. Если кто знает подскажите ПЛИЗ!.
2) Сохранить бы его в файл BMP, а потом грузануть его в «человеческий» класс для обработки потом обратно сохранить/загрузить в исходный BitmapSource. Опять же я лично ничего такого не нарыл.
3) Создать BitmapSource формата Bgra32 и отрисовать на нём исходный BitmapSource. Отредактировать новый BitmapSource и перерисовать обратно. Тут тоже я ничего не нарыл.
Остаётся тупая обезьянья работа по реализации ручками кучи форматов. Действительно при чём тут WPF, когда есть старое, доброе, шустрое и понятное WIN API.
Здравствуйте, KW_, Вы писали:
KW_>Остаётся тупая обезьянья работа по реализации ручками кучи форматов. Действительно при чём тут WPF, когда есть старое, доброе, шустрое и понятное WIN API.
Не компрометируйте Седой Урал. Юзайте FormatConvertedBitmap + WritableBitmap.
Но WritableBitmap тазже не дает прямого доступа к пикселю по его координатам.
S>Не компрометируйте Седой Урал. Юзайте FormatConvertedBitmap + WritableBitmap.
Проблема не в арифметике. Но не хочется делать лишних предположений относительно внутреннего устройства битмапа. Например — количество байт на пиксель. Короче говоря, хоть и уверен, что такой подход абсолютно неверен и кроссректален, решил конвертировать BitmapSource в старый Bitmap делать все манипуляции с ним и в конце конвертировать из Bitmap в BitmapSource.
S>Здравствуйте, 8086, Вы писали:
8>>Но WritableBitmap тазже не дает прямого доступа к пикселю по его координатам.
S>Но дает прямой доступ ко всему буферу. Чтобы получить доступ к пикселю по координатам нужно применить арифметику уровня 2-го класса.
Здравствуйте, 8086, Вы писали:
8>Проблема не в арифметике. Но не хочется делать лишних предположений относительно внутреннего устройства битмапа. Например — количество байт на пиксель.
Кол-во байт на пискель зависит от указанного формата точно так же как и в старом добром Bitmap-е.
8>Короче говоря, хоть и уверен, что такой подход абсолютно неверен и кроссректален, решил конвертировать BitmapSource в старый Bitmap делать все манипуляции с ним и в конце конвертировать из Bitmap в BitmapSource.
Ну это тоже вариант, особенно если нужна сила GDI при редактировании битмапа. Ничего кроссректального не вижу.
Здравствуйте, KW_, Вы писали:
KW_>Возможные выходы: KW_>1) Универсальное преобразования из одного формата в другой. Тогда можно ручками реализовать работу с Bgra32 а все другие форматы туда перегонять. Лично я не нашёл. Если кто знает подскажите ПЛИЗ!. KW_>2) Сохранить бы его в файл BMP, а потом грузануть его в «человеческий» класс для обработки потом обратно сохранить/загрузить в исходный BitmapSource. Опять же я лично ничего такого не нарыл. KW_>3) Создать BitmapSource формата Bgra32 и отрисовать на нём исходный BitmapSource. Отредактировать новый BitmapSource и перерисовать обратно. Тут тоже я ничего не нарыл.
На худой конец можно WIC попробовать. Managed обертки есть. Правда не знаю будет ли работать на чем-то кроме Windows 7.
Здравствуйте, 8086, Вы писали:
8>Как я понимаю, BitmapSource это "правильный" класс битмапов для использования в WPF. Неожиданно обнаружил, что BitmapSource не дает непосредственного доступа к пикселям, как старый Bitmap с его Set/GetPixel. Какой существует метод манипулирования битмапами в WPF. Может стоит взять вместо BitmapSource, что то другое?
Всем СПС за подсказки. Вот написал 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);
}
}