How can i fill part of image with color?
To floodfill an area you need a foodfill routine and very little else.
See this example:
It uses two pictureboxes, also a label to display the chosen color.
And two mouse click events, one to pick the color:
private void pictureBoxPalette_MouseClick(object sender, MouseEventArgs e)
{
Point sPt = scaledPoint(pictureBoxPalette, e.Location);
lbl_color.BackColor = ((Bitmap)pictureBoxPalette.Image).GetPixel(sPt.X, sPt.Y);
}
..and one to call the fill:
private void pictureBoxTgt_MouseClick(object sender, MouseEventArgs e)
{
Point sPt = scaledPoint(pictureBoxTgt, e.Location);
Bitmap bmp = (Bitmap)pictureBoxTgt.Image;
Color c0 = bmp.GetPixel(sPt.X, sPt.Y);
Fill4(bmp, sPt, c0, lbl_color.BackColor);
pictureBoxTgt.Image = bmp;
}
The Floodfill routine is taken from this post; it is basically a direct implementation of a wikipedia algorithm..:
static void Fill4(Bitmap bmp, Point pt, Color c0, Color c1)
{
Color cx = bmp.GetPixel(pt.X, pt.Y);
if (cx.GetBrightness() < 0.01f) return; // optional, to prevent filling a black grid
Rectangle bmpRect = new Rectangle(Point.Empty, bmp.Size);
Stack<Point> stack = new Stack<Point>();
int x0 = pt.X;
int y0 = pt.Y;
stack.Push(new Point(x0, y0) );
while (stack.Any() )
{
Point p = stack.Pop();
if (!bmpRect.Contains(p)) continue;
cx = bmp.GetPixel(p.X, p.Y);
if (cx.ToArgb() == c0.ToArgb()) //*
{
bmp.SetPixel(p.X, p.Y, c1);
stack.Push(new Point(p.X, p.Y + 1));
stack.Push(new Point(p.X, p.Y - 1));
stack.Push(new Point(p.X + 1, p.Y));
stack.Push(new Point(p.X - 1, p.Y));
}
}
}
Note: (*) Color equality will fail if one of the colors is a known or named color. So we need to convert to a common format..
Update
I have updated the code to include a function that will scale a mouse click location to an image pixel point; now it will work with SizeMode=StretchImage
as well, so you can work on the whole image..
static Point scaledPoint(PictureBox pb, Point pt)
{
float scaleX = 1f * pb.Image.Width / pb.ClientSize.Width;
float scaleY = 1f * pb.Image.Height / pb.ClientSize.Height;
return new Point((int)(pt.X * scaleX), (int)(pt.Y * scaleY));
}
Of course you can then save the Image.
Note that your original image is 4bpp and must be converted to 24bpp or better before coloring..
Also note that for SizeMode=Zoom
the calculations are a little more involved. Here is an example that should work with any SizeMode
.:
static Point scaledPoint(PictureBox pbox, Point pt)
{
Size si = pbox.Image.Size;
Size sp = pbox.ClientSize;
int left = 0;
int top = 0;
if (pbox.SizeMode == PictureBoxSizeMode.Normal ||
pbox.SizeMode == PictureBoxSizeMode.AutoSize) return pt;
if (pbox.SizeMode == PictureBoxSizeMode.CenterImage)
{
left = (sp.Width - si.Width) / 2;
top = (sp.Height - si.Height) / 2;
return new Point(pt.X - left, pt.Y - top);
}
if (pbox.SizeMode == PictureBoxSizeMode.Zoom)
{
if (1f * si.Width / si.Height < 1f * sp.Width / sp.Height)
left = (sp.Width - si.Width * sp.Height / si.Height) / 2;
else
top = (sp.Height - si.Height * sp.Width / si.Width) / 2;
}
pt = new Point(pt.X - left, pt.Y - top);
float scaleX = 1f * pbox.Image.Width / (pbox.ClientSize.Width - 2 * left) ;
float scaleY = 1f * pbox.Image.Height / (pbox.ClientSize.Height - 2 * top);
return new Point((int)(pt.X * scaleX), (int)(pt.Y * scaleY));
}
change particular area of an image and fill color in that area
Demo:
Full page: https://angularjs-rj88cu.stackblitz.io/
Code: https://stackblitz.com/edit/angularjs-rj88cu
Explanation:
The editor is an svg containing background image as <image>
and areas as <path>
both are created from editor.imageUrl
and editor.areas
For downloading,
- Background image is drawn on a canvas
- SVG (without
<image>
) is converted to dataUrl and then drawn on canvas - Canvas is converted to dataUrl for downloading
Why canvas was not used instead of svg in the first place?
Because mouse interaction are difficult to implement in canvas and is much easier in inline svg as the work like DOM elements (hover pseudo class, click events, etc)
Also I assumed you wanted angularjs because you have tagged it (even though you have not mentioned in the question)
Also there are a lot of bad practices in the code like editor not being a component and styling input[type=color]
's Shadow DOM (you can use some colorpicker plugin instead)
PS: Tell me if something needs to be changed
how to fill color in image in particular area?
I found the Solution with Flood fill algoritham
private void FloodFill(Bitmap bmp, Point pt, int targetColor, int replacementColor){
Queue<Point> q = new LinkedList<Point>();
q.add(pt);
while (q.size() > 0) {
Point n = q.poll();
if (bmp.getPixel(n.x, n.y) != targetColor)
continue;
Point w = n, e = new Point(n.x + 1, n.y);
while ((w.x > 0) && (bmp.getPixel(w.x, w.y) == targetColor)) {
bmp.setPixel(w.x, w.y, replacementColor);
if ((w.y > 0) && (bmp.getPixel(w.x, w.y - 1) == targetColor))
q.add(new Point(w.x, w.y - 1));
if ((w.y < bmp.getHeight() - 1)
&& (bmp.getPixel(w.x, w.y + 1) == targetColor))
q.add(new Point(w.x, w.y + 1));
w.x--;
}
while ((e.x < bmp.getWidth() - 1)
&& (bmp.getPixel(e.x, e.y) == targetColor)) {
bmp.setPixel(e.x, e.y, replacementColor);
if ((e.y > 0) && (bmp.getPixel(e.x, e.y - 1) == targetColor))
q.add(new Point(e.x, e.y - 1));
if ((e.y < bmp.getHeight() - 1)
&& (bmp.getPixel(e.x, e.y + 1) == targetColor))
q.add(new Point(e.x, e.y + 1));
e.x++;
}
}}
flood fill in android:
See this FloodFill
how to fill the transparent part of an RGBA image with color, given the xy touch position?
cv2
has cv2.floodFill()
It will get color in start_point
and search the same color in neighborhood and replace with color
import cv2
img_before = cv2.imread('image.png')
img_after = img_before.copy()
start_point = (400, 300)
color = (128, 128, 128)
cv2.floodFill(img_after, None, start_point, color)
cv2.imshow('before', img_before)
cv2.imshow('after', img_after)
cv2.waitKey(0)
cv2.destroyAllWindows()
but it works only with RGB
but not RBGA
so it would need more work.
pillow
also has ImageDraw.floodfill()
and it work with RGBA
. If you work with numpy.array
then it will need only to convert from array
to Image and later back to array
.
from PIL import Image, ImageDraw
img_before = Image.open('image.png')
img_after = img_before.copy()
start_point = (400, 300)
color = (128, 128, 128, 255)
img_draw = ImageDraw.floodfill(img_after, start_point, color, thresh=50)
img_before.show()
img_after.show()
#img_after.save('image_after.png')
Fill complex image with color
You could make a function that loops through all the pixels and fill them with black color.
BufferedImage image = ...
Color fillColor = new Color(0, 0, 0); // Black
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int color = image.getRGB(x, y);
int alpha = (color >> 24) & 0xff;
if (alpha == 255) {
image.setRGB(x, y, fillColor.getRGB());
}
}
}
Obviously, this will only work on fully opaque pixels. If the image on top contains some transparency, you can also change the condition to be more tolerant: if (alpha > 127)
. This will fill all pixels that are less than 50% transparent.
Related Topics
How to Implement Permission Based Access Control with ASP.NET Core
Returning a String from a C# Dll with Unmanaged Exports to Inno Setup Script
Unique Ways to Use the Null Coalescing Operator
System.Badimageformatexception: Reference Assemblies Should Not Be Loaded for Execution
How to Bind an Enum to a Dropdownlist Control in ASP.NET
Referenced Project Gets "Lost" at Compile Time
How to Check If a Number Is Positive or Negative in C#
How to Access Session in a Webmethod
Why Is a Round-Trip Conversion via a String Not Safe for a Double
Get Datetime.Now with Milliseconds Precision
JSON Deserialization - Map Array Indices to Properties with JSON.Net
Generate Class from Database Table
Is It Safe for Structs to Implement Interfaces
How to Create an Audit Trail with Entity Framework 5 and MVC 4
How to Test Your Request.Querystring[] Variables
Read a Xml (From a String) and Get Some Fields - Problems Reading Xml