GrayScale 변환

From YYpBD's MediaWiki

Jump to: navigation, search

RGB변환함수 -> GrayScale
function RGBToGray(RGBColor: TColor): TColor;
var
  Gray: Byte;
begin
  Gray := ((GetRValue(RGBColor) shl 5) + (GetGValue(RGBColor) shl 6) + (GetBValue(RGBColor) * 12)) div 108;
  result := RGB(Gray, Gray, Gray);
end;







제  목:[ 팁 ] RGB 를 GrayScale로 바꾸기..(수정)        관련자료:없음  [1427]
보낸이:백소영  (야그마당)  2000-04-12 16:58  조회:267  추천:1

안녕하세요.. 마당쇠 여덕수입니다..

컬러비트맵을 그레이스케일로 바꿔주는 함수입니다.
시행착오법에 의해 제가 구현할수 있는 가장 빠른 방법을 찾아서 올려드립니다.
혹시라도 더욱 빠르게 그레이스케일로 바꿀수 있는 방법이 있다면, 많은분들께
도움을 주시면 좋겠네요..


우선 RGB를 Grayscale로 바꾸는 방법에 대해서 정리를 하자면..

1.  gray := Round( ( 0.213 * B ) + ( 0.715 * G ) + ( 0.072 * R ) );

2.  gray := ((B * 12) + (G shl 6) + (R shl 5) ) div 108;

3.  gray := ( R + G + B ) div 3;

*4.  gray := ( R*3 + G*4 + B ) shr 3;

1의 방법이 가장 원안에 가까운 방법입니다만, 부동소숫점연산을 사용하다보니
무척이나 느리더군요..
2의 방법은 1의 방법과 비슷하나 정수연산으로 바꾸어 놓은 것이구요..
3의 방법은 가장 단순하고 무식한 방법입니다. 이론이구 뭐구 필요없이
RGB의 평균값을 얻어오는 것이죠..

* 4의 방법을 최정한(hanee)님께서 제시해 주셨습니다.
* 제 시스템에서 체크한 결과, 3의 방법보다 1.5배정도 더 빠르더군요.
* 최정한님께 감사드립니다.

제경우, 눈이 너무 무디어서 그런지 3의 방법으로 해도 만족할 정도의
그레이스케일이 나오더군요.. 물론 속도도 가장빠르구요..


그다음 속도문제때문에 가장 중요시 되는 부분이 각각의 점들의 색깔을
어떻게 가져오느냐는 것입니다.
물론 가장 쉬운 방법은 물론 Canvas.Pixels을 이용하는 것이겠지만...

  Color := Canvas.Pixels[x.y];

다들 아시다 시피 이녀석은 엄청나게 느린 방법이 되죠..
그다음 속도를 생각하시는 분들이라면 당연히 TBitmap.ScanLine 을 떠올리게
되겠죠..
메모리에 한줄씩 접근할수 있으므로 상당한 속도를 얻을수 있으나...
RGB를 조작하기에는 힘이 듭니다.
이런 이유에서인지 대부분의 그레이스케일 변환 루틴에서는 Canvas.Pixels을
이용하는 방법을 사용하더군요..

만약 ScanLine을 이용하고, RGB을 쉽게 조작할수 있다면 좋을텐데...
그러던차에 엄청난 편법을 발견했습니다.

PixelFormat을 변경해서 쉽게 RGB각각의 값을 조작할수 있는 방법이더군요..
궁하면 통한다는 말이 생각났습니다.
이런 가공할 편법에 힘입어서 더욱 고속화시키는데 주력해 봤습니다.

속도 향상을 위해서 RGB을 구조체로 만들고, 각각의 RGB에 접근할때 'with'문을
이용했습니다.
with 문을 안쓰고 RGB에 따로따로 접근할때보다, 속도가 엄청빨라지더군요.

다음은 위에서 주저리 주러리 늘어놓은 잡설을 구체화한 소스입니다.


type
  TRGB = record
    B, G, R : byte;
  end;

  PRGBArray = ^TRGBArray;
  TRGBArray = array[0..0] of TRGB;

procedure MakeGrayed(FGlyph, FGrayed : TBitmap);
var x,y,w,h:integer;
    gray : byte;
    p1, p2 : PRGBArray;
begin
  w := FGlyph.width;
  h := FGlyph.height;
  FGrayed.width := w;
  FGrayed.height:= h;

  FGlyph. PixelFormat := pf24bit;
  FGrayed.PixelFormat := pf24bit;

  for y:=0 to h-1 do
  begin
    p1 := FGlyph. ScanLine[y];
    p2 := FGrayed.ScanLine[y];
    for x:=0 to w-1 do
    begin

{
      //  가장 이론에 가까운 방법, but 속도은 꽝.
      with p1[x] do
        gray := Round( ( 0.213 * B ) +
                       ( 0.715 * G ) +
                       ( 0.072 * R ) );
{}
{
      //  이론에 가까우면서도 속도를 고려한 방법.
      with p1[x] do
        gray := ((B * 12) + (G shl 6) + (R shl 5) ) div 108;
{}
{
      //  속도만을 중시한 방법.
      with p1[x] do
        gray := ( R + G + B ) div 3;
{}
{}
      //  색상을 어느정도 유지하면서도 속도도 무척빠른 방법.
      //  최정한님께서 알려주셨습니다.
      with p1[x] do
        gray := ( R*3 + G*4 + B ) shr 3;
{}
      with p2[x] do
      begin
        R := gray;
        G := gray;
        B := gray;
      end;
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  MakeGrayed( Image1.Picture.Bitmap, Image2.Picture.Bitmap );
  Image2.Refresh;
end;


맞춤검색