#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2013
    Posts
    6
    Rep Power
    0

    Drawing a Image Histogram (RGB values)


    Hello,

    I'm I have .Jpeg images and I want to draw the pixels RGB values.
    At the moment I have this code to draw them in a Tchart:

    type
    PRGBTripleArray = ^TRGBTripleArray;
    TRGBTripleArray = array[0..4095] of TRGBTriple;

    procedure TForm1.Button1Click(Sender: TObject);
    var
    x, y, z : Integer;
    R, G, B : Byte;
    C : Tcolor;
    serieR : TChartSeries;
    serieG : TChartSeries;
    serieB : TChartSeries;
    serieRGB : TchartSeries;
    Bitmap : TBitmap;
    Pixels : PRGBTripleArray;

    VR, VG, VB : array of integer;


    begin

    Bitmap := TBitmap.Create;
    try
    Bitmap.Assign(Image1.Picture.Graphic);

    begin
    SetLength(VR, 256);
    SetLength(VG, 256);
    SetLength(VB, 256);


    serieR := TLineSeries.Create(nil);
    serieG := TLineSeries.Create(nil);
    serieB := TLineSeries.Create(nil);

    for Y := 0 to Bitmap.Height - 1 do
    begin
    Pixels := Bitmap.ScanLine[Y];
    for X := 0 to Bitmap.Width - 1 do
    begin

    // obtém as cores da paleta
    R := pixels[x].rgbtRed;
    G := pixels[x].rgbtGreen;
    B := pixels[x].rgbtBlue;
    C := RGB(
    Pixels[X].rgbtRed,
    Pixels[X].rgbtGreen,
    Pixels[X].rgbtBlue
    );


    VR[R] := VR[R] + 1;
    VG[G] := VR[G] + 1;
    VB[B] := VR[B] + 1;
    end;
    end;

    for z:= 0 to 255 do
    begin
    //cria grafico

    serieR.AddXY(Z, VR[z], '', clRed);
    serieG.AddXY(z, VG[z], '', clGreen);
    serieB.AddXY(z, VB[z], '', clBlue);




    end;

    serieR.Title := 'RED';
    serieG.Title := 'GREEN';
    serieB.Title := 'BLUE';

    Chart1.AddSeries(serieR);
    Chart2.AddSeries(serieG);
    Chart3.AddSeries(serieB);

    end;
    finally
    bitmap.free;
    end;



    end;



    But I keep getting wrong values, I need some suggestions to modify mt Code. I have Delphi XE3

    Thanks, frutasamir.
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    355
    Rep Power
    7
    I don't see where you set the initial values for the VR, VG, VB array elements to zero, so VR[R] := VR[R] + 1; VG[G] := VR[G] + 1; and VB[B] := VR[B] + 1; would have unpredictable results.

    You can either cycle through all values one by one setting them to 0, or you can use fillchar

    FillChar(VR, SizeOf(VR), 0);
    FillChar(VB, SizeOf(VB), 0);
    FillChar(VG, SizeOf(VG), 0);
    Last edited by majlumbo; March 13th, 2013 at 04:10 PM.
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2013
    Posts
    6
    Rep Power
    0
    I'm not quite sure what you mean, should I initialize the VR, VG, and VB before looping through the image?

    VR[R] := 0;
    VG[G] := 0;
    VB[B] := 0;
    for Y := 0 to Bitmap.Height - 1 do
    begin
    Pixels := Bitmap.ScanLine[Y];
    for X := 0 to Bitmap.Width - 1 do
    begin

    // obtém as cores da paleta
    R := pixels[x].rgbtRed;
    G := pixels[x].rgbtGreen;
    B := pixels[x].rgbtBlue;
    C := RGB(
    Pixels[X].rgbtRed,
    Pixels[X].rgbtGreen,
    Pixels[X].rgbtBlue
    );


    VR[R] := VR[R] + 1;
    VG[G] := VR[G] + 1;
    VB[B] := VR[B] + 1;
    end;
    end;

    How does the fillchar work?
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Regular (2000 - 2499 posts)

    Join Date
    Jan 2006
    Location
    Carlsbad, CA
    Posts
    2,057
    Rep Power
    383
    I am not sure exactly what you are doing but it seems to me that VR, VG and VB might need to be two dimension arrays.
    i.e. (look up the proper syntax please)
    setLength(VR,bitmap.Height,Bitmap.Width);

    Also, instead of
    VR[R] := VR[R] + 1;

    As you loop through the double loop, don't you need something like:
    VR[Y,X] := R;

    Or am I completely off the mark?

    Clive
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    355
    Rep Power
    7
    should I initialize the VR, VG, and VB before looping through the image?

    VR[R] := 0;
    VG[G] := 0;
    VB[B] := 0;
    Almost correct. VR, VG and VB are arrays each with 256 elements, so EACH ARRAY CELL needs to be set to zero before you attempt to add 1 one to it.
    either
    Code:
    for I := 0 to 255 do
    begin
       VR[I] := 0;
       VG[I] := 0;
       VB[I] := 0;
    end;
    or you can use fillchar,
    Code:
    fillchar(VR, sizeOf(VR), 0);
    fillchar(VG, sizeOf(VG), 0);
    fillchar(VB, sizeOf(VB), 0);
    Both options would have to occur AFTER you set the length of the array, but BEFORE you attempt to add a value to each cell to populate your histogram.
    Last edited by majlumbo; March 13th, 2013 at 08:40 PM.
  10. #6
  11. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2013
    Posts
    6
    Rep Power
    0
    I changed it, it's now like this....should work, but still getting wrong results on Tchart.


    VAR
    x, y, z : Integer;
    R, G, B : Byte;
    C : Tcolor;
    serieR : TChartSeries;
    serieG : TChartSeries;
    serieB : TChartSeries;
    serieRGB : TchartSeries;
    Bitmap : TBitmap;
    Pixels : PRGBTripleArray;

    VR, VG, VB : array[0..255] of integer;


    begin

    Bitmap := TBitmap.Create;
    try
    Bitmap.Assign(Image1.Picture.Graphic);

    begin
    //SetLength(VR, 256);
    //SetLength(VG, 256);
    //SetLength(VB, 256);
    fillchar(VR, sizeOf(VR), 0);
    fillchar(VG, sizeOf(VG), 0);
    fillchar(VB, sizeOf(VB), 0);

    serieR := TLineSeries.Create(nil);
    serieG := TLineSeries.Create(nil);
    serieB := TLineSeries.Create(nil);

    for Y := 0 to Bitmap.Height - 1 do
    begin
    Pixels := Bitmap.ScanLine[Y];
    for X := 0 to Bitmap.Width - 1 do
    begin

    // obtém as cores da paleta
    R := pixels[x].rgbtRed;
    G := pixels[x].rgbtGreen;
    B := pixels[x].rgbtBlue;



    VR[R] := VR[R]+1;
    VG[G] := VR[G]+1;
    VB[B] := VR[B]+1;

    end;
    end;

    for z:= 0 to 255 do
    begin
    //cria grafico

    serieR.AddXY(Z, VR[z], '', RGB(255, 0,0 ));
    serieG.AddXY(z, VG[z], '', RGB(0,235,0));
    serieB.AddXY(z, VB[z], '', RGB(0,0,255));




    end;

    serieR.Title := 'RED';
    serieG.Title := 'GREEN';
    serieB.Title := 'BLUE';

    Chart1.AddSeries(serieR);
    Chart1.AddSeries(serieG);
    Chart1.AddSeries(serieB);

    end;
    finally
    bitmap.free;
    end;



    end;
  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    355
    Rep Power
    7
    Originally Posted by Frutasamir
    //SetLength(VR, 256);
    //SetLength(VG, 256);
    //SetLength(VB, 256);
    Why did you comment out setting the array size?

    I said that setting the cell values to zero had to happen AFTER setting the length. Now you aren't setting the length of the array at all.

    as far as setting the color of your series, you are setting all your Red series to just Red, Green to just Green and Blue to just blue. If instead you want to show the shade of each color, then this may possibly do it (untested...)

    Code:
    serieR.AddXY(Z, VR[z], '', RGB(z,0,0));
    serieG.AddXY(z, VG[z], '', RGB(0,z,0));
    serieB.AddXY(z, VB[z], '', RGB(0,0,z));

    You may also have a possible memory leak in your code:

    Code:
    serieR := TLineSeries.Create(nil);
    serieG := TLineSeries.Create(nil);
    serieB := TLineSeries.Create(nil);
    passing nil as a parameter to your line series' Create method tells the compiler that you are managing the lifetime of these objects. Much like you do with your bitmap (you create it and at the end of the procedure you free it.)

    If you already have code in your application that frees SerieR, SerieG and SerieB, such as in your applications OnDestroy or your form's OnClose your OK, but if not, you need to either add it or pass in the object that you want to take responsibility to free your series.
    Code:
    SerieX := TLineSeries.Create(Chart1);//Chart1 will free your series when it itself is freed
    Last edited by majlumbo; March 15th, 2013 at 11:55 AM.
  14. #8
  15. No Profile Picture
    Contributing User
    Devshed Regular (2000 - 2499 posts)

    Join Date
    Jan 2006
    Location
    Carlsbad, CA
    Posts
    2,057
    Rep Power
    383
    Code:
    // obtém as cores da paleta
    R := pixels[x].rgbtRed;
    G := pixels[x].rgbtGreen;
    B := pixels[x].rgbtBlue;
    I believe that the above code does not guarantee that all possible array indexes of R,G,B will be visited. So the code below will not apply to every position in the arrays.
    Code:
    VR[R] := VR[R]+1;
    VG[G] := VR[G]+1;
    VB[B ] := VR[B ]+1;
    As long as that is your intention, that is fine.
    It will ensure that every unvisited VR[index] position in your z loop will be:
    serieR.AddXY(Z, 0, '', RGB(255, 0,0 ));
    While every visited position with a rgbtRed of 0 will be:
    serieR.AddXY(Z, 1, '', RGB(255, 0,0 ));


    Clive
  16. #9
  17. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2013
    Posts
    6
    Rep Power
    0
    Thank you for the suggestions, So now I have the code like this:

    type
    PRGBTripleArray = ^TRGBTripleArray;
    TRGBTripleArray = array[0..20000] of TRGBTriple;

    procedure TForm1.Button1Click(Sender: TObject);
    var
    x, y, z : Integer;
    R, G, B : Byte;
    C : Tcolor;
    serieR : TChartSeries;
    serieG : TChartSeries;
    serieB : TChartSeries;
    serieRGB : TchartSeries;
    Bitmap : TBitmap;
    Pixels : PRGBTripleArray;

    VR, VG, VB : array of integer; //Can I do something like: array [0..255] of Integer????


    begin
    SetLength(VR, 255);
    SetLength(VG, 255);
    setLength(VB, 255);
    Bitmap := TBitmap.Create;
    try
    Bitmap.Assign(Image1.Picture.Graphic);

    begin


    //fillchar(VR, sizeOf(VR), 0); (if I include them in the code, it will give an error and won't work)
    //fillchar(VG, sizeOf(VG), 0);
    //fillchar(VB, sizeOf(VB), 0);

    //serieR := TLineSeries.Create(nil);
    //serieG := TLineSeries.Create(nil);
    //serieB := TLineSeries.Create(nil);
    SerieR := TLineSeries.Create(Chart1);
    SerieG := TLineSeries.Create(Chart1);
    SerieB := TLineSeries.Create(Chart1);

    for Y := 0 to Bitmap.Height - 1 do
    begin
    Pixels := Bitmap.ScanLine[Y];
    for X := 0 to Bitmap.Width - 1 do
    begin

    // obtém as cores da paleta
    R := pixels[x].rgbtRed;
    G := pixels[x].rgbtGreen;
    B := pixels[x].rgbtBlue;


    VR[R] := VR[R]+1;
    VG[G] := VR[G]+1;
    VB[B] := VR[B]+1;


    end;
    end;

    for z:= 0 to 255 do
    begin
    //cria grafico

    serieR.AddXY(Z, VR[z], '', RGB(z,0,0)); // It changes its color intensity, very cool.
    serieG.AddXY(z, VG[z], '', RGB(0,z,0));
    serieB.AddXY(z, VB[z], '', RGB(0,0,z));





    end;

    serieR.Title := 'RED';
    serieG.Title := 'GREEN';
    serieB.Title := 'BLUE';

    Chart1.AddSeries(serieR);
    Chart1.AddSeries(serieG);
    Chart1.AddSeries(serieB);

    end;
    finally
    bitmap.free;


    end;



    end;


    It just reads small images, and still not giving logical results....
  18. #10
  19. No Profile Picture
    Contributing User
    Devshed Regular (2000 - 2499 posts)

    Join Date
    Jan 2006
    Location
    Carlsbad, CA
    Posts
    2,057
    Rep Power
    383
    Because I am probably not sure exactly what you are trying to do all I am saying may not be of value.
    1. You certainly can use the old fixed array construct as you never change the size of the array. But you do need to initialize each slot.
    2. Are you making sure that the bitmap is being set to 32 (is it pf32?) I don't have Delphi available on my iPad.

    What is illogical (inaccurate) in your results?

    Clive
  20. #11
  21. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    355
    Rep Power
    7
    Originally Posted by Frutasamir
    VR, VG, VB : array of integer; //Can I do something like: array [0..255] of Integer????
    Yes, if you do
    Code:
    VR array [0..255] of integer;
    VG array [0..255] of integer;
    VB array [0..255] of integer;
    then you don't have to do the setlength...

    Originally Posted by Frutasamir
    fillchar(VR, sizeOf(VR), 0); (if I include them in the code, it will give an error and won't work)
    if this gives an error (although you didn't mention what the error is), then the fall back is to do a loop, setting each cell value to zero.
    Code:
    for I := 0 to 255 do
    begin
       VR[I] := 0;
       VG[I] := 0;
       VB[I] := 0;
    end;
    again, setting the values must be done before you attempt to add 1 to an array cell value. If you declare the array with dimensions, you don't have to worry about doing it after setting the array length as mentioned above.

    And to reiterate Clive's point
    Are you making sure that the bitmap is being set to 32 (is it pf32?),
    After you assign the bitmap to the image set the pixelformat

    Code:
    Bitmap := TBitmap.Create;
    try
      Bitmap.Assign(Image1.Picture.Graphic);
      Image1.PixelFormat := pf32bit;//I think this is what you need to do
      ...
    Last edited by majlumbo; March 18th, 2013 at 08:17 AM.

IMN logo majestic logo threadwatch logo seochat tools logo