Программно меняйте цвета с загруженного растрового изображения на красный, зеленый, синий или серый, пиксель за пикселем - PullRequest
2 голосов
/ 10 января 2011

Скачать исходный код здесь: http://www.eyeClaxton.com/download/delphi/ColorSwap.zip

Да, я хочу преобразовать что-то "в основном голубое" во что-то "в основном зеленое".

Я хотел бы взять исходное растровое изображение (светло-голубой) и изменить цвета (Pixel by Pixel) на отношение эквивалентности красного, зеленого, синего и серого. Чтобы понять, что я имею в виду, я включил исходный код и снимок экрана. Любая помощь будет принята с благодарностью. Если вам нужна дополнительная информация, пожалуйста, не стесняйтесь спрашивать.

Если бы вы могли взглянуть на приведенный ниже код, у меня есть три функции, по которым я ищу помощи. Функции «RGBToRed, RGBToGreen и RGBToRed», кажется, не могут придумать правильные формулы.

alt text

unit MainUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TMainFrm = class(TForm)
    Panel1: TPanel;
    Label1: TLabel;
    Panel2: TPanel;
    Label2: TLabel;
    Button1: TButton;
    BeforeImage1: TImage;
    AfterImage1: TImage;
    RadioGroup1: TRadioGroup;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainFrm: TMainFrm;

implementation

{$R *.DFM}
function RGBToGray(RGBColor: TColor): TColor;
var
  Gray: Byte;
begin
  Gray := Round(
    (0.90 * GetRValue(RGBColor)) +
    (0.88 * GetGValue(RGBColor)) +
    (0.33 * GetBValue(RGBColor)));

  Result := RGB(Gray, Gray, Gray);
end;

function RGBToRed(RGBColor: TColor): TColor;
var
  Red: Byte;
begin
  // Not sure of the algorithm for this color
  Result := RGB(Red, Red, Red);
end;

function RGBToGreen(RGBColor: TColor): TColor;
var
  Green: Byte;
begin
  // Not sure of the algorithm for this color
  Result := RGB(Green, Green, Green);
end;

function RGBToBlue(RGBColor: TColor): TColor;
var
  Blue: Byte;
begin
  // Not sure of the algorithm for this color
  Result := RGB(Blue, Blue, Blue);
end;

procedure TMainFrm.FormCreate(Sender: TObject);
begin
  BeforeImage1.Picture.LoadFromFile('Images\RightCenter.bmp');
end;

procedure TMainFrm.Button1Click(Sender: TObject);
var
  Bitmap: TBitmap;
  I, X: Integer;
  Color: Integer;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.LoadFromFile('Images\RightCenter.bmp');

    for X := 0 to Bitmap.Height do
    begin
      for I := 0 to Bitmap.Width do
      begin
        Color := ColorToRGB(Bitmap.Canvas.Pixels[I, X]);

        case Color of
          $00000000: ;   // Skip any Color Here!
        else
          case RadioGroup1.ItemIndex of
            0: Bitmap.Canvas.Pixels[I, X] := RGBToBlue(Color);
            1: Bitmap.Canvas.Pixels[I, X] := RGBToRed(Color);
            2: Bitmap.Canvas.Pixels[I, X] := RGBToGreen(Color);
            3: Bitmap.Canvas.Pixels[I, X] := RGBToGray(Color);
          end;
        end;
      end;
    end;
    AfterImage1.Picture.Graphic := Bitmap;
  finally
    Bitmap.Free;
  end;
end;

end.

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

alt text

alt text

Ответы [ 2 ]

13 голосов
/ 10 января 2011

Понятия не имею, чего вы пытаетесь достичь. Во-первых, какое отношение этот вопрос имеет к отношениям эквивалентности ?

Во-вторых, код

function RGBToRed(RGBColor: TColor): TColor;
var
  Red: Byte;
begin
  // Not sure of the algorithm for this color
  Result := RGB(Red, Red, Red);
end;

бессмысленно. Возвращаемое значение не определено. Функции RGB принимают интенсивность красного, зеленого и синего в диапазоне от 0 до 255 и возвращают TColor с этими компонентами. Например, RGB(255, 0, 0) - это чисто красный, то есть 255 красных, 0 зеленых и 0 синих. В приведенном выше коде, однако, Red не инициализируется, поэтому Result может быть любого серого цвета (в зависимости от того, что происходит с Red). [И, как вы, вероятно, знаете, если вы смешиваете равное количество красного, зеленого и синего, как вы делаете, вы получаете серый цвет.] Например, Red может оказаться 45 раз при запуске приложения, а затем результатом будет серый цвет RGB(45, 45, 45). В следующий раз это может быть 163.

Может быть, вам нужна функция, которая принимает TColor и возвращает ее красный компонент? Тогда подойдет GetRValue, и, конечно же, есть GetGValue и GetBValue.

Или, может быть, вы хотите создать серый цвет из заданного TColor, как вы это делаете, когда используете Photoshop для создания полутонового изображения из цветного изображения. Затем, если Color является TColor, соответствующее значение серого будет

grey := (GetRValue(Color) + GetGValue(Color) + GetBValue(Color)) div 3;

Есть и другие способы сделать это (в результате получаются слегка отличающиеся изображения в оттенках серого, но это самый простой (и самый быстрый)). (Например, вы можете преобразовать цвет из RGB в HSV или HSL, а затем установить насыщенность равной нулю.)

Обновление

Я думаю, что вижу, что вы хотите сделать. Может быть, вы хотите извлечь красный, зеленый и синий каналы. То есть вы хотите преобразовать каждый пиксель RGB(R, G, B) в RGB(R, 0, 0) в красном корпусе. Если это то, что вы хотите сделать, то вы должны сделать

function RGBToRed(RGBColor: TColor): TColor;
begin
  Result := RGB(GetRValue(RGBColor), 0, 0);
end;

и аналогично для других компонентов, то есть

function RGBToGreen(RGBColor: TColor): TColor;
begin
  Result := RGB(0, GetGValue(RGBColor), 0);
end;

и

function RGBToBlue(RGBColor: TColor): TColor;
begin
  Result := RGB(0, 0, GetBValue(RGBColor));
end;

Или, может быть

Или, может быть, вы просто хотите сделать изображение серым, а затем «покрасить» его красным (или зеленым, или синим). Тогда вам нужна тяжелая техника. Теоретически вы хотите заменить каждый пиксель с HSV(h, s, v) на HSV(0, s, v) в красном корпусе, HSV(120, s, v) в зеленом корпусе и HSV(240, s, v) в синем корпусе. Или, может быть, вы также хотите установить насыщенность s в 1.

Я использую следующие процедуры для преобразования между RGB и HSV:

function RGBToHSV(const Color: TRGB): THSV;
var
  cmax, cmin, cdiff: real;
begin
  cmax := MaxComponent(Color);
  cmin := MinComponent(Color);
  cdiff := cmax - cmin;

  with Color, result do
  begin

    // Hue
    if cmax = cmin then
      hsvHue := 0
    else if cmax = rgbRed then
      hsvHue := (60 * (rgbGreen - rgbBlue) / cdiff)
    else if cmax = rgbGreen then
      hsvHue := (60 * (rgbBlue - rgbRed) / cdiff) + 120
    else
      hsvHue := (60 * (rgbRed - rgbGreen) / cdiff) + 240;

    hsvHue := Fix360(hsvHue);

    // Saturation
    if cmax = 0 then
      hsvSaturation := 0
    else
      hsvSaturation := 1 - cmin / cmax;

    // Value
    hsvValue := cmax;

  end;

end;

и

function HSVToRGB(const Color: THSV): TRGB;
var
  hi: integer;
  f, q, p, t: real;
begin

  with Color do
  begin

    hi := floor(hsvHue / 60) mod 6;
    f := hsvHue / 60 - floor(hsvHue / 60);
    p := hsvValue * (1 - hsvSaturation);
    q := hsvValue * (1 - f * hsvSaturation);
    t := hsvValue * (1 - (1 - f) * hsvSaturation);

    case hi of
      0: result := RGB(hsvValue, t, p);
      1: result := RGB(q, hsvValue, p);
      2: result := RGB(p, hsvValue, t);
      3: result := RGB(p, q, hsvValue);
      4: result := RGB(t, p, hsvValue);
      5: result := RGB(hsvValue, p, q);
    end;

  end;

end;

, где

type
  TRGB = record
    rgbRed,            // red intensity between 0 and 1
    rgbGreen,          // green intensity between 0 and 1
    rgbBlue: double;   // blue intensity between 0 and 1
  end;

  THSV = record
    hsvHue,            // hue angle between 0 and 360
    hsvSaturation,     // saturation between 0 (grey) and 1 (full colour)
    hsvValue: double;  // value between 0 (dark) and 1 ("normal")
  end;

Конечный результат в зеленом случае будет выглядеть как

Цветовой тон зафиксирован на 120 http://english.rejbrand.se/algosim/manual/pmproc/fixHue120.jpg

в первом случае (оттенок установлен на 120) и

Оттенки зафиксированы на 120, а насыщенность - на 1 http://english.rejbrand.se/algosim/manual/pmproc/monochromatic120.jpg

в последнем случае (оттенок установлен на 120, насыщенность установлена ​​на 1).

Или, возможно

Ваш вопрос очень смелый, даже с правкой. Вы хотите преобразовать что-то «в основном синее» во что-то «в основном зеленое». Но есть тысячи способов сделать это! Конечно, очень простой способ - просто поменять синий и зеленый каналы, то есть заменить RGB(r, g, b) на RGB(r, b, g) или, явно,

function SwapBlueGreen(Color: TColor): TColor;
begin
  result := RGB(GetRValue(Color), GetBValue(Color), GetGValue(Color));
end;

Тогда вы получите что-то вроде

Смена каналов R и G http://english.rejbrand.se/algosim/manual/pmproc/swapRG.jpg

где красный и зеленый каналы поменялись местами. Обратите внимание, что красная палочка теперь зеленая, а зеленая трава теперь красная. Что белое остается белым.

Наконец

Добро пожаловать в мир растровых манипуляций! Есть много простых и забавных вещей. Еще несколько моих примеров: http://english.rejbrand.se/algosim/manual/pmproc/pmproc.html

2 голосов
/ 10 января 2011

Доступ к информации о цвете через свойство Pixels очень медленный. Вам следует рассмотреть возможность использования свойства "Scanline", чтобы получить код примерно на 2 уровня быстрее. http://blogs.embarcadero.com/pawelglowacki/2010/04/15/39051 содержит примеры кода, как это сделать.

Успех!

...