Использование оттенков серого в растровом изображении - PullRequest
1 голос
/ 06 ноября 2010

В настоящее время, когда я хочу добавить серый цвет на растровое изображение, я использую следующее

Bitmap.SetPixel(x,y, new Color.FromArgb(100,100,100);

Мой вопрос больше. Он использует несколько шагов, которые, на мой взгляд, не требуются:

  1. У нас есть цвет в оттенках серого: 100
  2. Мне нужно преобразовать его в настоящий цвет RGB (100,100,100)
  3. Затем он вычисляет, какой цвет выходит из него, и снова это шкала серого (100)

* 1015 запроса на *

Есть ли что-то подобное

Bitmap.SetPixel(x,y, new GreyColor(100));

Ответы [ 3 ]

2 голосов
/ 06 ноября 2010

Нет, в .NET не существует такой вещи, как GreyColor. GreyColor - это частный случай цвета, точно так же, как зеленый лайм является частным случаем цвета. Нет необходимости усложнять модель.

Есть некоторые предопределенные цвета , которые вы можете использовать, если сможете найти нужный вам цвет. Они просто возвращают объект Color, значение ARGB которого совпадает с предопределенным цветом, поэтому они на самом деле не делают ничего, отличного от того, что делает ваш код выше. Они просто для удобства программиста, который не может или не хочет запоминать не очень интуитивную цветовую модель RGB.

Кажется, вы обеспокоены потенциальными расходами или избыточностью создания объекта Color, но есть несколько причин, по которым это не должно вызывать беспокойства. Вы можете думать о методе FromArgb как о перегруженном конструкторе для объекта Color: это способ создания цвета, отличного от предопределенных. Он просто создает цветовой объект, который определяется как заданные вами значения RGB. На самом деле нет другого способа представления цвета (кроме альтернативных цветовых моделей, таких как HSV, но даже тогда вам придется указывать три отдельных значения: оттенок 0, насыщенность 0 и значение 39 ). Мы, люди, можем описывать цвета гораздо более абстрактными способами, но компьютер на самом деле не понимает и не заботится об этих вещах. Документация для самой структуры Color объясняет, что она просто представляет значения RGB вместе со значением альфа (прозрачности). Это очень маленький объект, который содержит около 4 байтовых значений. И именно так метод, которым вы передаете цвет (Bitmap.SetPixel), будет рисовать цвет, основываясь на этих значениях красного, зеленого и синего.

Стоит также отметить, что Color является структурой, которая является типом значения, а не ссылочным типом. Это означает, среди прочего , что он живет inline, где бы ни была определена ваша переменная. Он не указывает на другой объект, он живет локально в стеке, а не в куче, и стек дешев . Это делает его как минимум несколько менее дорогим, чем ссылочный тип, такой как класс.

Наконец, я был бы упущен, если бы не указал, что этот тип микрооптимизации редко будет иметь какое-либо значение для производительности вашего кода.

Короче говоря, нет такой вещи, но беспокоиться не о чем.

2 голосов
/ 06 ноября 2010

Нет ничего встроенного для преобразования значения яркости в цвет.

Чтобы получить яркость цвета, используйте метод GetBrightness. Возвращает значение от 0,0 до 1,0, поэтому вам нужно увеличить его, чтобы получить значение 0-255:

Color c = betPixel(x, y);
int brightness = (int)Math.Round(c.GetBrightness() * 255.0);

Вы можете сделать методы расширения, чтобы сделать код проще:

public static ColorExtensions {

  public static Color ToGrayscale(this int brightness) {
    return Color.FromArgb(brightness, brightness, brightness);
  }

  public static int FromGrayscale(this Color color) {
    return (int)Math.Round(color.GetBrightness() * 255.0);
  }

}

Использование:

bitmap.SetPixel(x, y, 100.ToGrayscale());
int g = bitmap.GetPixel(x, y).FromGrayscale();
1 голос
/ 06 ноября 2010

Вы можете создавать 8-битные индексированные растровые изображения и работать с ними:

var bmp = new Bitmap(width, height,
    System.Drawing.Imaging.PixelFormat.Format8bppIndexed);

Вы должны будете написать свои собственные методы получения / установки пикселей, например, (не проверенные, но должны дать общее представление о том, как это сделать):

public static byte GetIndexedPixel(this Bitmap bitmap, int x, int y)
{
    var bd = bitmap.LockBits(new Rectangle(x, y, 1, 1),
        System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
    var color = System.Runtime.InteropServices.Marshal.ReadByte(bd.Scan0);
    bitmap.UnlockBits(bd);
    return color;
}

public static void SetIndexedPixel(this Bitmap bitmap, int x, int y, byte color)
{
    var bd = bitmap.LockBits(new Rectangle(x, y, 1, 1),
        System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
    System.Runtime.InteropServices.Marshal.WriteByte(bd.Scan0, color);
    bitmap.UnlockBits(bd);
}

Как правило, я не думаю, что вам следует это делать, если вы не генерируете в памяти байтовые массивы пикселей в градациях серого и хотите преобразовать их в растровое изображение за несколько вызовов CopyMemory. Однако если вы используете SetPixel / GetPixel, производительность может не быть проблемой.

...