change a bitmap's pixel colour

前端 未结 4 1755
鱼传尺愫
鱼传尺愫 2020-12-10 07:04

I am trying to change a bit-map\'s pixel color if it\'s white. I wrote following code. But it\'s awfully slow!. i want to check if a pixel\'s color is white or not, and if i

相关标签:
4条回答
  • 2020-12-10 07:36
        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");
        }
    
    0 讨论(0)
  • 2020-12-10 07:39

    You should use scanlines for this. Example:

    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;
    

    To try this:

    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;
    

    Update: You need only a very minor modification of the code to make it work on 32-bit bitmaps instead:

    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;
    

    In fact, you could do

    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;
    

    if you don't want to make a single procedure that works with both 24-bit and 32-bit bitmaps, as TLama did. [One benefit of having two separate procedures is that these short procedures are easier to read (and maintain).]

    0 讨论(0)
  • 2020-12-10 07:45
    procedure TForm1.Button1Click(Sender: TObject);
    var
      BitMap1,
      BitMap2 : TBitmap;
      X, Y, Size : Integer;
    
      P: Cardinal;
    begin
      BitMap1 := TBitmap.Create;
      BitMap1.LoadFromFile('image1.bmp');
      BitMap1.Transparent := true;
      BitMap1.TransparentColor := clWhite; // old color
    
      BitMap2 := TBitMap.Create;
      BitMap2.Height := BitMap1.Height;
      BitMap2.Width := BitMap1.Width;
      BitMap2.Canvas.Brush.Color := clBlack; // new color
      BitMap2.Canvas.FillRect(
       Rect(
         0,
         0,
         BitMap2.Width,
         BitMap2.Height
        )
      );
     
      BitMap2.Canvas.Draw(BitMap1);
      
      Image1.Picture.Assign(BitMap2);
      
      BitMap1.Free;
      BitMap2.Freel
    end;
    
    0 讨论(0)
  • 2020-12-10 07:51

    For sure use the ScanLine property to access bitmap pixels since you're working with a large array of pixels where the Pixels access is slow. For replacing colors of your choice with support for 24-bit and 32-bit bitmaps, I would use something like this:

    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;
    

    And the usage:

    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;
    

    For pure GDI and bitmaps having at most 256 colors you might use the CreateMappedBmp function.

    0 讨论(0)
提交回复
热议问题