2013-03-08 157 views
6

我試圖改變位圖的像素顏色,如果它是白色的。我寫了下面的代碼。但它非常緩慢!我想檢查像素的顏色是否是白色,如果是白色,請將顏色更改爲黑色。更改位圖的像素顏色

有人可以提出更好的方法嗎?

procedure TForm1.Button1Click(Sender: TObject); 
var 
    BitMap1 : TBitmap; 
    X, Y, Size : Integer; 

    P: Cardinal; 
begin 
    BitMap1 := TBitmap.Create; 
    bitMap1.LoadFromFile('image1.bmp'); 

    for Y := 0 to Bitmap1.Height - 1 do 
    begin 
    for X := 0 to Bitmap1.width * size - 1 do 
    begin 
    p := BitMap1.Canvas.Pixels[X,Y]; 
    if p = 255 then 
     BitMap1.Canvas.Pixels[X,Y] := 0; 

    end; 
    end; 

    Image1.Picture.Assign(BitMap1); 
end; 
+1

爲了快速訪問位圖像素,使用'ScanLine'屬性。您可能會發現某些內容需要閱讀['here'](http://stackoverflow.com/q/13583451/960757)。 – TLama 2013-03-08 15:32:24

+1

@TLama精彩的教程。非常感謝 – jimsweb 2013-03-08 15:39:51

+0

很高興喜歡它! – TLama 2013-03-08 15:40:33

回答

7

您應該爲此使用掃描線。例如:

procedure ChangeWhiteToBlack(var Bitmap: TBitmap); 
var 
    scanline: PRGBTriple; 
    y: Integer; 
    x: Integer; 
begin 
    Assert(Bitmap.PixelFormat = pf24bit); 
    for y := 0 to Bitmap.Height - 1 do 
    begin 
    scanline := Bitmap.ScanLine[y]; 
    for x := 0 to Bitmap.Width - 1 do 
    begin 
     with scanline^ do 
     begin 
     if (rgbtBlue = 255) and (rgbtGreen = 255) and (rgbtRed = 255) then 
      FillChar(scanline^, sizeof(TRGBTriple), 0); 
     end; 
     inc(scanline); 
    end; 
    end; 
end; 

要試試這個:

procedure TForm5.FormCreate(Sender: TObject); 
var 
    bm: TBitmap; 
begin 
    bm := TBitmap.Create; 
    try 
    bm.LoadFromFile('C:\Users\Andreas Rejbrand\Desktop\test.bmp'); 
    ChangeWhiteToBlack(bm); 
    bm.SaveToFile('C:\Users\Andreas Rejbrand\Desktop\test2.bmp'); 
    finally 
    bm.Free; 
    end; 
end; 

更新:你只需要代碼的一個非常小的修改,以使其在32位的位圖,而不是工作:

procedure ChangeWhiteToBlack32(var Bitmap: TBitmap); 
var 
    scanline: PRGBQuad; 
    y: Integer; 
    x: Integer; 
begin 
    Assert(Bitmap.PixelFormat = pf32bit); 
    for y := 0 to Bitmap.Height - 1 do 
    begin 
    scanline := Bitmap.ScanLine[y]; 
    for x := 0 to Bitmap.Width - 1 do 
    begin 
     with scanline^ do 
     begin 
     if (rgbBlue = 255) and (rgbGreen = 255) and (rgbRed = 255) then 
      FillChar(scanline^, sizeof(TRGBQuad), 0); 
     end; 
     inc(scanline); 
    end; 
    end; 
end; 

其實你可以做

procedure ChangeWhiteToBlack24(var Bitmap: TBitmap); 
var 
    scanline: PRGBTriple; 
    y: Integer; 
    x: Integer; 
begin 
    Assert(Bitmap.PixelFormat = pf24bit); 
    for y := 0 to Bitmap.Height - 1 do 
    begin 
    scanline := Bitmap.ScanLine[y]; 
    for x := 0 to Bitmap.Width - 1 do 
    begin 
     with scanline^ do 
     begin 
     if (rgbtBlue = 255) and (rgbtGreen = 255) and (rgbtRed = 255) then 
      FillChar(scanline^, sizeof(TRGBTriple), 0); 
     end; 
     inc(scanline); 
    end; 
    end; 
end; 

procedure ChangeWhiteToBlack32(var Bitmap: TBitmap); 
var 
    scanline: PRGBQuad; 
    y: Integer; 
    x: Integer; 
begin 
    Assert(Bitmap.PixelFormat = pf32bit); 
    for y := 0 to Bitmap.Height - 1 do 
    begin 
    scanline := Bitmap.ScanLine[y]; 
    for x := 0 to Bitmap.Width - 1 do 
    begin 
     with scanline^ do 
     begin 
     if (rgbBlue = 255) and (rgbGreen = 255) and (rgbRed = 255) then 
      FillChar(scanline^, sizeof(TRGBQuad), 0); 
     end; 
     inc(scanline); 
    end; 
    end; 
end; 

procedure ChangeWhiteToBlack(var Bitmap: TBitmap); 
begin 
    case Bitmap.PixelFormat of 
    pf24bit: ChangeWhiteToBlack24(Bitmap); 
    pf32bit: ChangeWhiteToBlack32(Bitmap); 
    else 
    raise Exception.Create('Pixel format must be pf24bit or pf32bit.'); 
    end; 
end; 

如果您不想製作一個適用於24位和32位位圖的單個過程,如TLama所做的那樣。 [有兩個單獨程序的好處是這些簡單的程序更容易閱讀(並保持)。]

+0

如果Bitmap.PixelFormat = pf32bit不會工作嗎? – jimsweb 2013-03-08 16:37:16

+1

@ jimsweb:不會。但是,將其適應32位情況所需的更改很小。 – 2013-03-08 16:38:24

10

爲了確保使用ScanLine屬性來訪問位圖像素,因爲你有一個大的像素陣列,其中Pixels訪問速度很慢的工作。對於支持更換你選擇的顏色爲24位和32位的位圖,我會用這樣的:

procedure ReplaceColor(ABitmap: TBitmap; ASource, ATarget: TColor); 
type 
    TRGBBytes = array[0..2] of Byte; 
var 
    I: Integer; 
    X: Integer; 
    Y: Integer; 
    Size: Integer; 
    Pixels: PByteArray; 
    SourceColor: TRGBBytes; 
    TargetColor: TRGBBytes; 
const 
    TripleSize = SizeOf(TRGBBytes); 
begin 
    case ABitmap.PixelFormat of 
    pf24bit: Size := TripleSize; 
    pf32bit: Size := SizeOf(TRGBQuad); 
    else 
    raise Exception.Create('Bitmap must be 24-bit or 32-bit format!'); 
    end; 

    for I := 0 to TripleSize - 1 do 
    begin 
    // fill the array of bytes with color channel values in BGR order, 
    // the same would do for the SourceColor from ASource parameter: 
    // SourceColor[0] := GetBValue(ASource); 
    // SourceColor[1] := GetGValue(ASource); 
    // SourceColor[2] := GetRValue(ASource); 
    // but this is (just badly readable) one liner 
    SourceColor[I] := Byte(ASource shr (16 - (I * 8))); 
    // the same do for the TargetColor array from the ATarget parameter 
    TargetColor[I] := Byte(ATarget shr (16 - (I * 8))); 
    end; 

    for Y := 0 to ABitmap.Height - 1 do 
    begin 
    // get a pointer to the currently iterated row pixel byte array 
    Pixels := ABitmap.ScanLine[Y]; 
    // iterate the row horizontally pixel by pixel 
    for X := 0 to ABitmap.Width - 1 do 
    begin 
     // now imagine, that you have an array of bytes in which the groups of 
     // bytes represent a single pixel - e.g. the used Pixels array for the 
     // first 2 pixels might look like this for 24-bit and 32-bit bitmaps: 

     // Pixels [0][1][2]  [3][4][5] 
     // 24-bit B G R  B G R 
     // Pixels [0][1][2][3] [4][5][6][7] 
     // 32-bit B G R A B G R A 

     // from the above you can see that you'll need to multiply the current 
     // pixel iterator by the count of color channels to point to the first 
     // (blue) color channel in that array; and that's what that (X * Size) 
     // is for here; X is a pixel iterator, Size is size of a single pixel:   

     // X * 3 (0 * 3)  (1 * 3) 
     //   ⇓    ⇓ 
     // Pixels [0][1][2]  [3][4][5] 
     // 24-bit B G R  B G R 

     // X * 4 (0 * 4)  (1 * 4) 
     //   ⇓    ⇓ 
     // Pixels [0][1][2][3] [4][5][6][7] 
     // 32-bit B G R A B G R A 

     // so let's compare a BGR value starting at the (X * Size) position of 
     // the Pixels array with the SourceColor array and if it matches we've 
     // found the same colored pixel, if so then... 
     if CompareMem(@Pixels[(X * Size)], @SourceColor, TripleSize) then 
     // copy the TargetColor color byte array values to that BGR position 
     // (in other words, replace the color channel bytes there) 
     Move(TargetColor, Pixels[(X * Size)], TripleSize); 
    end; 
    end; 
end; 

和使用:

procedure TForm1.Button1Click(Sender: TObject); 
var 
    Bitmap: TBitmap; 
begin 
    Bitmap := TBitmap.Create; 
    try 
    Bitmap.LoadFromFile('d:\Image.bmp'); 
    ReplaceColor(Bitmap, clWhite, clBlack); 
    Image1.Picture.Assign(Bitmap); 
    finally 
    Bitmap.Free; 
    end; 
end; 

對於純GDI和最多有256種顏色的位圖可以使用CreateMappedBmp函數。

+1

這隻適用於位圖是24位的,當然。至少應該包含一個'Assert'。 – 2013-03-08 15:30:47

+0

@Andreas,現在好點? – TLama 2013-03-08 16:03:58

+0

+1是的,現在你涵蓋了這兩種情況。 – 2013-03-08 16:27:29

0
private void btnLoad2_Click(object sender, System.EventArgs e) 
    { 
     Bitmap myBitmap= new Bitmap(openFileDialog1.FileName); 
     Bitmap myBitmap1 = new Bitmap("C:\\Documents and Settings\\Lalji\\Desktop\\image.png"); 
     for (int x = 0; x < myBitmap.Width; x++) 
     { 
      for (int y = 0; y < myBitmap.Height; y++) 
      { 
       // Get the color of a pixel within myBitmap. 
       Color pixelColor = myBitmap.GetPixel(x, y); 
       string pixelColorStringValue = 
        pixelColor.R.ToString("D3") + " " + 
        pixelColor.G.ToString("D3") + " " + 
        pixelColor.B.ToString("D3") + ", "; 
       if (pixelColor.R.Equals(0) && pixelColor.G.Equals(0) && pixelColor.B.Equals(0)) 
       { 
        //MessageBox.Show("black pixel"); 
       } 

       else if (pixelColor.R.Equals(255) && pixelColor.G.Equals(255) && pixelColor.B.Equals(255)) 
       { 
        //MessageBox.Show("white pixel"); 
        myBitmap1.SetPixel(x, y, Color.White); 
       } 
       //switch (pixelColorStringValue) 
       //{ 
       // case "255 255 255": 
       //  { 
       //   // white pixel 
       //   MessageBox.Show("white pixel"); 
       //   break; 
       //  } 
       // case "000 000 000,": 
       //  { 
       //   // black pixel 
       //   MessageBox.Show("black pixel"); 
       //   break; 
       //  } 
       //} 
      } 
     } 

     myBitmap1.Save("C:\\Documents and Settings\\Lalji\\Desktop\\image1.png"); 
     MessageBox.Show("Process done"); 
    }