ZifNab
asked on
Color a region of picture
Hi,
Does somebody know if this is possible and how?
1) I've a picture (let it be .jpg (or something else)
2) Read it into Delphi (shouldn't be a problem :-) )
3) The image is a map of a country and now I want to color the regions according to some values.
4) How can a color these regions?
Regards, Zif.
Does somebody know if this is possible and how?
1) I've a picture (let it be .jpg (or something else)
2) Read it into Delphi (shouldn't be a problem :-) )
3) The image is a map of a country and now I want to color the regions according to some values.
4) How can a color these regions?
Regards, Zif.
note :
for each color you'd need a seperate HRGN;
for each color you'd need a seperate HRGN;
You could take a look here http://www.jps.net/gfody/ too.
There's greats components there to help you work with images in pixel by pixel basis
and they're really fast and come with source code.
Tchau,
Reginaldo
There's greats components there to help you work with images in pixel by pixel basis
and they're really fast and come with source code.
Tchau,
Reginaldo
you can cheat if it is 16+ bit color
just draw the borders of the region using a color that is TColor(color_of_map) + 1. The borders won't be seen because there is barely any difference in color. Then floodfill the center with whatever color you want.
just draw the borders of the region using a color that is TColor(color_of_map) + 1. The borders won't be seen because there is barely any difference in color. Then floodfill the center with whatever color you want.
ASKER
... The problem is here :
I don't draw the borders of the regions! They are already one the picture! What I'm thinking of is that I use a sort of Floodfill. I give the centre op the region the the function and this one colors the region... (but the region isn't drawn, it's already on the map...
Zif.
I don't draw the borders of the regions! They are already one the picture! What I'm thinking of is that I use a sort of Floodfill. I give the centre op the region the the function and this one colors the region... (but the region isn't drawn, it's already on the map...
Zif.
If I understood the Q right this should be quite simple. If you have the color of the region and if the region is the same color all over, then you could just put in in a loop to compare the pixels colors one by one with the known color. I think there's a function called something like GetPixelColor() or similar.
regards,
Johan
regards,
Johan
I think that sageryd means...
Image1.Canvas.Pixels[x, y]
which returns or sets (depending on how you use it) the color of a particular pixel.
I don't think this is what you're after though. I think you're asking for a floodfill routine. Am I right?
Image1.Canvas.Pixels[x, y]
which returns or sets (depending on how you use it) the color of a particular pixel.
I don't think this is what you're after though. I think you're asking for a floodfill routine. Am I right?
ASKER
Well, .... yes, I'm looking for a floodfill routine....
But, I could make a floodfill routine out of this proposition... However, not that easy I guess...
Are there are other solutions?
Regards, Tom.
But, I could make a floodfill routine out of this proposition... However, not that easy I guess...
Are there are other solutions?
Regards, Tom.
Why not take the regions as different images and then you could place them all together and be able to change the colour of an individual part without worrying about the others or flooding or anything like that!
listening..
procedure TForm1.FillRegion(ColorToF ill, NewColor: TColor);
var
X, Y: integer;
begin
Image.Canvas.Brush.Color := ColorToFill;
for Y := 1 to Image.Height do begin
for X := 1 to Image.Width do begin
if Image.Canvas.Pixels[X, Y] = ColorToFill then begin
Image.Canvas.Pixels[X, Y] := NewColor;
//Application.ProcessMessa ges;
end;
end;
end;
end;
This works if the color is the same all over the area to be filled...
//johan
var
X, Y: integer;
begin
Image.Canvas.Brush.Color := ColorToFill;
for Y := 1 to Image.Height do begin
for X := 1 to Image.Width do begin
if Image.Canvas.Pixels[X, Y] = ColorToFill then begin
Image.Canvas.Pixels[X, Y] := NewColor;
//Application.ProcessMessa
end;
end;
end;
end;
This works if the color is the same all over the area to be filled...
//johan
ASKER
sageryd,
yes, this should work... thought of it also, but for this I should calculate all rectangles. Image is made of lot's of regions, thus lot of figuring out. Since I'm lazy, I would rewrite your function by which it only has to use one coördinate.
Jaymol,
this isn't that easy, This is lot's of work... Look at it like this. The image displays a map (eg America). I then have to cut-out all the states (and perhaps their different regions too).... not that good solution.
Zif.
yes, this should work... thought of it also, but for this I should calculate all rectangles. Image is made of lot's of regions, thus lot of figuring out. Since I'm lazy, I would rewrite your function by which it only has to use one coördinate.
Jaymol,
this isn't that easy, This is lot's of work... Look at it like this. The image displays a map (eg America). I then have to cut-out all the states (and perhaps their different regions too).... not that good solution.
Zif.
Zif...I think I've got a solution here.... :)
procedure TForm1.FillRegion(ColorToF ill: TColor; Clrs: array of TColor);
var
X, Y, C, A: integer;
begin
C := 0;
A := 0;
Image.Canvas.Brush.Color := ColorToFill;
for Y := 0 to Image.Height do begin
for X := 0 to Image.Width do begin
if Image.Canvas.Pixels[X, Y] = ColorToFill then begin
if C = 1 then C := 0 else C := 1;
Image.Canvas.Brush.Color := Clrs[C];
Image.Canvas.FloodFill(X, Y, Image.Canvas.Pixels[X, Y], fsSurface);
Inc(A);
end;
end;
end;
Image.Canvas.Brush.Color := clRed;
Image.Canvas.TextOut(0, 0, 'Amount of fields colored: ' + IntToStr(A));
end;
procedure TForm1.Button1Click(Sender : TObject);
begin
Button1.Enabled := False;
FillRegion(clGreen, [clTeal, clYellow]);
Button1.Caption := 'Done!';
end;
What do you think?
cheers,
Johan
procedure TForm1.FillRegion(ColorToF
var
X, Y, C, A: integer;
begin
C := 0;
A := 0;
Image.Canvas.Brush.Color := ColorToFill;
for Y := 0 to Image.Height do begin
for X := 0 to Image.Width do begin
if Image.Canvas.Pixels[X, Y] = ColorToFill then begin
if C = 1 then C := 0 else C := 1;
Image.Canvas.Brush.Color := Clrs[C];
Image.Canvas.FloodFill(X, Y, Image.Canvas.Pixels[X, Y], fsSurface);
Inc(A);
end;
end;
end;
Image.Canvas.Brush.Color := clRed;
Image.Canvas.TextOut(0, 0, 'Amount of fields colored: ' + IntToStr(A));
end;
procedure TForm1.Button1Click(Sender
begin
Button1.Enabled := False;
FillRegion(clGreen, [clTeal, clYellow]);
Button1.Caption := 'Done!';
end;
What do you think?
cheers,
Johan
ASKER
Johan, sorry to reject your answer, but there is something I don't understand. Look at the picture as a map :
Everything has got the color white, only the regions are drawn as black lines.
Let us say that I want to fill region of Ohio with color red and region Washington with color green.
How does your function know which region to fill?
..... mmm.... I should have a look at this FloodFill.... this seems to be the one I need.... Does it also works on images? And not just on rectangles etc drawn on the Canvas...
Everything has got the color white, only the regions are drawn as black lines.
Let us say that I want to fill region of Ohio with color red and region Washington with color green.
How does your function know which region to fill?
..... mmm.... I should have a look at this FloodFill.... this seems to be the one I need.... Does it also works on images? And not just on rectangles etc drawn on the Canvas...
Well if you draw on the Canvas of an image it works, yes...
Well if you specify a coordinate within Washington or Ohio there should be no problem, otherwise I believe it's impossible. How could the procedure know by itself which region is Washington?
If you don't just need different color of all the states and you don't care which region gets which color, then it's already almost built, just add more colors to the existing array input of the procudere and remove this line:
if C = 1 then C := 0 else C := 1;
regards,
Johan
Well if you specify a coordinate within Washington or Ohio there should be no problem, otherwise I believe it's impossible. How could the procedure know by itself which region is Washington?
If you don't just need different color of all the states and you don't care which region gets which color, then it's already almost built, just add more colors to the existing array input of the procudere and remove this line:
if C = 1 then C := 0 else C := 1;
regards,
Johan
Correction:
If you don't care which regions get which colors in the input array of the procedure, just add more colors to the array and remove the line above!
If you don't care which regions get which colors in the input array of the procedure, just add more colors to the array and remove the line above!
Zif?
ASKER
oops, sorry... I'm on vacation...
thanks for your help. It seems that floodfill works on bitmaps files and not on other formats.
Regards, Tom.
thanks for your help. It seems that floodfill works on bitmaps files and not on other formats.
Regards, Tom.
ASKER
sageryd,
please answer question,
Zif
please answer question,
Zif
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
;)
i didnt want to point to component but i think you like the demo of this one:
http://sunsite.icm.edu.pl/delphi/ftp/d20free/hotimage.zip
or else you probably be needing something like the folowing demo(from tomes graphical)where you create a "hotspot" region with tpoints a paint method :
unit CreatePolyPolygonRgnU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
HotSpotRgn: HRGN; // holds the multiple polygon region
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var
PolyPoints: array[0..11] of TPoint; // holds the points of the polygons
VertexCounts: array[0..1] of Integer; // holds the vertex counts
begin
{define one polygon in the region}
PolyPoints[0] := Point(68, 80);
PolyPoints[1] := Point(76, 72);
PolyPoints[2] := Point(87, 80);
PolyPoints[3] := Point(86, 96);
PolyPoints[4] := Point(100, 96);
PolyPoints[5] := Point(100, 160);
PolyPoints[6] := Point(68, 160);
{define another polygon in the region}
PolyPoints[7] := Point(173, 53);
PolyPoints[8] := Point(184, 66);
PolyPoints[9] := Point(184, 146);
PolyPoints[10] := Point(160, 146);
PolyPoints[11] := Point(160, 66);
{indicate that the firs polygon consists of 7 points, and the second
consists of 5 points}
VertexCounts[0] := 7;
VertexCounts[1] := 5;
{create the multiple polygon region}
HotSpotRgn := CreatePolyPolygonRgn(PolyP
end;
procedure TForm1.Button1Click(Sender
begin
{invert the area defined by the multiple polygon region}
InvertRgn(Canvas.Handle, HotSpotRgn);
end;
procedure TForm1.Image1MouseDown(Sen
Shift: TShiftState; X, Y: Integer);
var
TranslatedPt: TPoint; // holds a form specific coordinate
begin
{since the region is defined in logical coordinates relative to the form,
the indicated location of the mouse click must be translated appropriately}
TranslatedPt := Image1.ClientToScreen(Poin
TranslatedPt := Form1.ScreenToClient(Trans
{indicate if the point is within the 'hotspot' area defined by the
multiple polygon region}
if PtInRegion(HotSpotRgn, TranslatedPt.X, TranslatedPt.Y) then
Caption := 'Clicked on a hotspot'
else
Caption := 'No hot spot clicked';
end;
procedure TForm1.FormDestroy(Sender:
begin
{delete the region}
DeleteObject(HotSpotRgn);
end;
end.
hope that helps some
Regards Barry