This commit is contained in:
Ingy döt Net 2013-04-10 16:19:29 -07:00
parent e5e8880e41
commit 518da4a923
1019 changed files with 15877 additions and 0 deletions

View file

@ -0,0 +1,8 @@
Implement a [[wp:flood fill|flood fill]].
A flood fill is a way of filling an area using ''color banks'' to define the contained area or a ''target color'' which "determines" the area (the ''valley'' that can be flooded; Wikipedia uses the term ''target color''). It works almost like a water flooding from a point towards the banks (or: inside the valley): if there's a hole in the banks, the flood is not contained and all the image (or all the "connected valleys") get filled.
To accomplish the task, you need to implement just one of the possible algorithms (examples are on [[wp:flood fill|Wikipedia]]). Variations on the ''theme'' are allowed (e.g. adding a tolerance parameter or argument for color-matching of the ''banks'' or ''target'' color).
[[Image:Unfilledcirc.png|128px|thumb|right]]
'''Testing''': the basic algorithm is not suitable for ''truecolor'' images; a possible test image is the one shown on the right box; you can try to fill the white area, or the black inner circle.

View file

@ -0,0 +1,4 @@
---
category:
- Graphics algorithms
note: Raster graphics operations

View file

@ -0,0 +1,106 @@
procedure Flood_Fill
( Picture : in out Image;
From : Point;
Fill : Pixel;
Replace : Pixel;
Distance : Luminance := 20
) is
function Diff (A, B : Luminance) return Luminance is
pragma Inline (Diff);
begin
if A > B then
return A - B;
else
return B - A;
end if;
end Diff;
function "-" (A, B : Pixel) return Luminance is
pragma Inline ("-");
begin
return Luminance'Max (Luminance'Max (Diff (A.R, B.R), Diff (A.G, B.G)), Diff (A.B, B.B));
end "-";
procedure Column (From : Point);
procedure Row (From : Point);
Visited : array (Picture'Range (1), Picture'Range (2)) of Boolean :=
(others => (others => False));
procedure Column (From : Point) is
X1 : Positive := From.X;
X2 : Positive := From.X;
begin
Visited (From.X, From.Y) := True;
for X in reverse Picture'First (1)..From.X - 1 loop
exit when Visited (X, From.Y);
declare
Color : Pixel renames Picture (X, From.Y);
begin
Visited (X, From.Y) := True;
exit when Color - Replace > Distance;
Color := Fill;
X1 := X;
end;
end loop;
for X in From.X + 1..Picture'Last (1) loop
exit when Visited (X, From.Y);
declare
Color : Pixel renames Picture (X, From.Y);
begin
Visited (X, From.Y) := True;
exit when Color - Replace > Distance;
Color := Fill;
X2 := X;
end;
end loop;
for X in X1..From.X - 1 loop
Row ((X, From.Y));
end loop;
for X in From.X + 1..X2 loop
Row ((X, From.Y));
end loop;
end Column;
procedure Row (From : Point) is
Y1 : Positive := From.Y;
Y2 : Positive := From.Y;
begin
Visited (From.X, From.Y) := True;
for Y in reverse Picture'First (2)..From.Y - 1 loop
exit when Visited (From.X, Y);
declare
Color : Pixel renames Picture (From.X, Y);
begin
Visited (From.X, Y) := True;
exit when Color - Replace > Distance;
Color := Fill;
Y1 := Y;
end;
end loop;
for Y in From.Y + 1..Picture'Last (2) loop
exit when Visited (From.X, Y);
declare
Color : Pixel renames Picture (From.X, Y);
begin
Visited (From.X, Y) := True;
exit when Color - Replace > Distance;
Color := Fill;
Y2 := Y;
end;
end loop;
for Y in Y1..From.Y - 1 loop
Column ((From.X, Y));
end loop;
for Y in From.Y + 1..Y2 loop
Column ((From.X, Y));
end loop;
end Row;
Color : Pixel renames Picture (From.X, From.Y);
begin
if Color - Replace <= Distance then
Visited (From.X, From.Y) := True;
Color := Fill;
Column (From);
end if;
end Flood_Fill;

View file

@ -0,0 +1,19 @@
declare
File : File_Type;
begin
Open (File, In_File, "Unfilledcirc.ppm");
declare
Picture : Image := Get_PPM (File);
begin
Close (File);
Flood_Fill
( Picture => Picture,
From => (122, 30),
Fill => (255,0,0),
Replace => White
);
Create (File, Out_File, "Filledcirc.ppm");
Put_PPM (File, Picture);
Close (File);
end;
end;

View file

@ -0,0 +1,41 @@
SetBatchLines, -1
CoordMode, Mouse
CoordMode, Pixel
CapsLock::
KeyWait, CapsLock
MouseGetPos, X, Y
PixelGetColor, color, X, Y
FloodFill(x, y, color, 0x000000, 1, "CapsLock")
MsgBox Done!
Return
FloodFill(x, y, target, replacement, mode=1, key="")
{
If GetKeyState(key, "P")
Return
PixelGetColor, color, x, y
If (color <> target || color = replacement || target = replacement)
Return
VarSetCapacity(Rect, 16, 0)
NumPut(x, Rect, 0)
NumPut(y, Rect, 4)
NumPut(x+1, Rect, 8)
NumPut(y+1, Rect, 12)
hDC := DllCall("GetDC", UInt, 0)
hBrush := DllCall("CreateSolidBrush", UInt, replacement)
DllCall("FillRect", UInt, hDC, Str, Rect, UInt, hBrush)
DllCall("ReleaseDC", UInt, 0, UInt, hDC)
DllCall("DeleteObject", UInt, hBrush)
FloodFill(x+1, y, target, replacement, mode, key)
FloodFill(x-1, y, target, replacement, mode, key)
FloodFill(x, y+1, target, replacement, mode, key)
FloodFill(x, y-1, target, replacement, mode, key)
If (mode = 2 || mode = 4)
FloodFill(x, y, target, replacement, mode, key)
If (Mode = 3 || mode = 4)
{
FloodFill(x+1, y+1, target, replacement, key)
FloodFill(x-1, y+1, target, replacement, key)
FloodFill(x+1, y-1, target, replacement, key)
FloodFill(x-1, y-1, target, replacement, key)
}
}

View file

@ -0,0 +1,55 @@
#NoEnv
#SingleInstance, Force
SetBatchLines, -1
CoordMode, Mouse
CoordMode, Pixel
return
CapsLock::
KeyWait, CapsLock
MouseGetPos, X, Y
PixelGetColor, color, X, Y
FloodFill(x, y, color, 0x000000, 1, "Esc")
MsgBox Done!
Return
FloodFill( 0x, 0y, target, replacement, mode=1, key="" )
{
VarSetCapacity(Rect, 16, 0)
hDC := DllCall("GetDC", UInt, 0)
hBrush := DllCall("CreateSolidBrush", UInt, replacement)
l := 0
while l >= 0
{
if getkeystate(key, "P")
return
x := %l%x, y := %l%y
%l%p++
p := %l%p
PixelGetColor, color, x, y
if (color = target)
{
NumPut(x, Rect, 0)
NumPut(y, Rect, 4)
NumPut(x+1, Rect, 8)
NumPut(y+1, Rect, 12)
DllCall("FillRect", UInt, hDC, Str, Rect, UInt, hBrush)
}
else if (p = 1)
{
%l%x := %l%y := %l%p := "", l--
continue
}
if (p < 5)
ol := l++
, %l%x := %ol%x + (p = 1 ? 1 : p = 2 ? -1 : 0)
, %l%y := %ol%y + (p = 3 ? 1 : p = 4 ? -1 : 0)
else
%l%x := %l%y := %l%p := "", l--
}
DllCall("ReleaseDC", UInt, 0, UInt, hDC)
DllCall("DeleteObject", UInt, hBrush)
}

View file

@ -0,0 +1,24 @@
MODE 8
GCOL 15
CIRCLE FILL 640, 512, 500
GCOL 0
CIRCLE FILL 500, 600, 200
GCOL 3
PROCflood(600, 200, 15)
GCOL 4
PROCflood(600, 700, 0)
END
DEF PROCflood(X%, Y%, C%)
LOCAL L%, R%
IF POINT(X%,Y%) <> C% ENDPROC
L% = X%
R% = X%
WHILE POINT(L%-2,Y%) = C% : L% -= 2 : ENDWHILE
WHILE POINT(R%+2,Y%) = C% : R% += 2 : ENDWHILE
LINE L%,Y%,R%,Y%
FOR X% = L% TO R% STEP 2
PROCflood(X%, Y%+2, C%)
PROCflood(X%, Y%-2, C%)
NEXT
ENDPROC

View file

@ -0,0 +1,106 @@
// http://commons.wikimedia.org/wiki/File:Julia_immediate_basin_1_3.png
unsigned int f(unsigned int _iX, unsigned int _iY)
/*
gives position of point (iX,iY) in 1D array ; uses also global variables
it does not check if index is good so memory error is possible
*/
{return (_iX + (iYmax-_iY-1)*iXmax );}
int FillContour(int iXseed, int iYseed, unsigned char color, unsigned char _data[])
{
/*
fills contour with black border ( color = iJulia) using seed point inside contour
and horizontal lines
it starts from seed point, saves max right( iXmaxLocal) and max left ( iXminLocal) interior points of horizontal line,
in new line ( iY+1 or iY-1) it computes new interior point : iXmidLocal=iXminLocal + (iXmaxLocal-iXminLocal)/2;
result is stored in _data array : 1D array of 1-bit colors ( shades of gray)
it does not check if index of _data array is good so memory error is possible
*/
int iX, /* seed integer coordinate */
iY=iYseed,
/* most interior point of line iY */
iXmidLocal=iXseed,
/* min and max of interior points of horizontal line iY */
iXminLocal,
iXmaxLocal;
int i ; /* index of _data array */;
/* --------- move up --------------- */
do{
iX=iXmidLocal;
i =f(iX,iY); /* index of _data array */;
/* move to right */
while (_data[i]==iInterior)
{ _data[i]=color;
iX+=1;
i=f(iX,iY);
}
iXmaxLocal=iX-1;
/* move to left */
iX=iXmidLocal-1;
i=f(iX,iY);
while (_data[i]==iInterior)
{ _data[i]=color;
iX-=1;
i=f(iX,iY);
}
iXminLocal=iX+1;
iY+=1; /* move up */
iXmidLocal=iXminLocal + (iXmaxLocal-iXminLocal)/2; /* new iX inside contour */
i=f(iXmidLocal,iY); /* index of _data array */;
if ( _data[i]==iJulia) break; /* it should not cross the border */
} while (iY<iYmax);
/* ------ move down ----------------- */
iXmidLocal=iXseed;
iY=iYseed-1;
do{
iX=iXmidLocal;
i =f(iX,iY); /* index of _data array */;
/* move to right */
while (_data[i]==iInterior) /* */
{ _data[i]=color;
iX+=1;
i=f(iX,iY);
}
iXmaxLocal=iX-1;
/* move to left */
iX=iXmidLocal-1;
i=f(iX,iY);
while (_data[i]==iInterior) /* */
{ _data[i]=color;
iX-=1; /* move to right */
i=f(iX,iY);
}
iXminLocal=iX+1;
iY-=1; /* move down */
iXmidLocal=iXminLocal + (iXmaxLocal-iXminLocal)/2; /* new iX inside contour */
i=f(iXmidLocal,iY); /* index of _data array */;
if ( _data[i]==iJulia) break; /* it should not cross the border */
} while (0<iY);
/* mark seed point by big pixel */
const int iSide =iXmax/500; /* half of width or height of big pixel */
for(iY=iYseed-iSide;iY<=iYseed+iSide;++iY){
for(iX=iXseed-iSide;iX<=iXseed+iSide;++iX){
i= f(iX,iY); /* index of _data array */
_data[i]=10;}}
return 0;
}

View file

@ -0,0 +1,9 @@
/* #include <sys/queue.h> */
typedef struct {
color_component red, green, blue;
} rgb_color;
typedef rgb_color *rgb_color_p;
void floodfill(image img, int px, int py,
rgb_color_p bankscolor,
rgb_color_p rcolor);

View file

@ -0,0 +1,93 @@
#include "imglib.h"
typedef struct _ffill_node {
int px, py;
TAILQ_ENTRY(_ffill_node) nodes;
} _ffill_node_t;
TAILQ_HEAD(_ffill_queue_s, _ffill_node);
typedef struct _ffill_queue_s _ffill_queue;
inline void _ffill_removehead(_ffill_queue *q)
{
_ffill_node_t *n = q->tqh_first;
if ( n != NULL ) {
TAILQ_REMOVE(q, n, nodes);
free(n);
}
}
inline void _ffill_enqueue(_ffill_queue *q, int px, int py)
{
_ffill_node_t *node;
node = malloc(sizeof(_ffill_node_t));
if ( node != NULL ) {
node->px = px; node->py = py;
TAILQ_INSERT_TAIL(q, node, nodes);
}
}
inline double color_distance( rgb_color_p a, rgb_color_p b )
{
return sqrt( (double)(a->red - b->red)*(a->red - b->red) +
(double)(a->green - b->green)*(a->green - b->green) +
(double)(a->blue - b->blue)*(a->blue - b->blue) ) / (256.0*sqrt(3.0));
}
inline void _ffill_rgbcolor(image img, rgb_color_p tc, int px, int py)
{
tc->red = GET_PIXEL(img, px, py)[0];
tc->green = GET_PIXEL(img, px, py)[1];
tc->blue = GET_PIXEL(img, px, py)[2];
}
#define NSOE(X,Y) do { \
if ( ((X)>=0)&&((Y)>=0) && ((X)<img->width)&&((Y)<img->height)) { \
_ffill_rgbcolor(img, &thisnode, (X), (Y)); \
if ( color_distance(&thisnode, bankscolor) > tolerance ) { \
if (color_distance(&thisnode, rcolor) > 0.0) { \
put_pixel_unsafe(img, (X), (Y), rcolor->red, \
rcolor->green, \
rcolor->blue); \
_ffill_enqueue(&head, (X), (Y)); \
pixelcount++; \
} \
} \
} \
} while(0)
unsigned int floodfill(image img, int px, int py,
rgb_color_p bankscolor,
rgb_color_p rcolor)
{
_ffill_queue head;
rgb_color thisnode;
unsigned int pixelcount = 0;
double tolerance = 0.05;
if ( (px < 0) || (py < 0) || (px >= img->width) || (py >= img->height) )
return;
TAILQ_INIT(&head);
_ffill_rgbcolor(img, &thisnode, px, py);
if ( color_distance(&thisnode, bankscolor) <= tolerance ) return;
_ffill_enqueue(&head, px, py);
while( head.tqh_first != NULL ) {
_ffill_node_t *n = head.tqh_first;
_ffill_rgbcolor(img, &thisnode, n->px, n->py);
if ( color_distance(&thisnode, bankscolor) > tolerance ) {
put_pixel_unsafe(img, n->px, n->py, rcolor->red, rcolor->green, rcolor->blue);
pixelcount++;
}
int tx = n->px, ty = n->py;
_ffill_removehead(&head);
NSOE(tx - 1, ty);
NSOE(tx + 1, ty);
NSOE(tx, ty - 1);
NSOE(tx, ty + 1);
}
return pixelcount;
}

View file

@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdlib.h>
#include "imglib.h"
int main(int argc, char **argv)
{
image animage;
rgb_color ic;
rgb_color rc;
if ( argc > 1 ) {
animage = read_image(argv[1]);
if ( animage != NULL ) {
ic.red = 255; /* = 0; */
ic.green = 255; /* = 0; */
ic.blue = 255; /* = 0; */
rc.red = 0;
rc.green = 255;
rc.blue = 0;
floodfill(animage, 100, 100, &ic, &rc);
/* 150, 150 */
print_jpg(animage, 90);
free(animage);
}
}
return 0;
}

View file

@ -0,0 +1,28 @@
: third 2 pick ;
: 3dup third third third ;
: 4dup 2over 2over ;
: flood ( color x y bmp -- )
3dup b@ >r ( R: color to fill )
4dup b!
third 0 > if
rot 1- -rot
3dup b@ r@ = if recurse then
rot 1+ -rot
then
third 1+ over bwidth < if
rot 1+ -rot
3dup b@ r@ = if recurse then
rot 1- -rot
then
over 0 > if
swap 1- swap
3dup b@ r@ = if recurse then
swap 1+ swap
then
over 1+ over bheight < if
swap 1+ swap
3dup b@ r@ = if recurse then
swap 1- swap
then
r> drop ;

View file

@ -0,0 +1,106 @@
module RCImageArea
use RCImageBasic
use RCImagePrimitive
implicit none
real, parameter, private :: matchdistance = 0.2
private :: northsouth, eastwest
contains
subroutine northsouth(img, p0, tcolor, fcolor)
type(rgbimage), intent(inout) :: img
type(point), intent(in) :: p0
type(rgb), intent(in) :: tcolor, fcolor
integer :: npy, spy, y
type(rgb) :: pc
npy = p0%y - 1
do
if ( inside_image(img, p0%x, npy) ) then
call get_pixel(img, p0%x, npy, pc)
if ( ((pc .dist. tcolor) > matchdistance ) .or. ( pc == fcolor ) ) exit
else
exit
end if
npy = npy - 1
end do
npy = npy + 1
spy = p0%y + 1
do
if ( inside_image(img, p0%x, spy) ) then
call get_pixel(img, p0%x, spy, pc)
if ( ((pc .dist. tcolor) > matchdistance ) .or. ( pc == fcolor ) ) exit
else
exit
end if
spy = spy + 1
end do
spy = spy - 1
call draw_line(img, point(p0%x, spy), point(p0%x, npy), fcolor)
do y = min(spy, npy), max(spy, npy)
if ( y == p0%y ) cycle
call eastwest(img, point(p0%x, y), tcolor, fcolor)
end do
end subroutine northsouth
subroutine eastwest(img, p0, tcolor, fcolor)
type(rgbimage), intent(inout) :: img
type(point), intent(in) :: p0
type(rgb), intent(in) :: tcolor, fcolor
integer :: npx, spx, x
type(rgb) :: pc
npx = p0%x - 1
do
if ( inside_image(img, npx, p0%y) ) then
call get_pixel(img, npx, p0%y, pc)
if ( ((pc .dist. tcolor) > matchdistance ) .or. ( pc == fcolor ) ) exit
else
exit
end if
npx = npx - 1
end do
npx = npx + 1
spx = p0%x + 1
do
if ( inside_image(img, spx, p0%y) ) then
call get_pixel(img, spx, p0%y, pc)
if ( ((pc .dist. tcolor) > matchdistance ) .or. ( pc == fcolor ) ) exit
else
exit
end if
spx = spx + 1
end do
spx = spx - 1
call draw_line(img, point(spx, p0%y), point(npx, p0%y), fcolor)
do x = min(spx, npx), max(spx, npx)
if ( x == p0%x ) cycle
call northsouth(img, point(x, p0%y), tcolor, fcolor)
end do
end subroutine eastwest
subroutine floodfill(img, p0, tcolor, fcolor)
type(rgbimage), intent(inout) :: img
type(point), intent(in) :: p0
type(rgb), intent(in) :: tcolor, fcolor
type(rgb) :: pcolor
if ( .not. inside_image(img, p0%x, p0%y) ) return
call get_pixel(img, p0%x, p0%y, pcolor)
if ( (pcolor .dist. tcolor) > matchdistance ) return
call northsouth(img, p0, tcolor, fcolor)
call eastwest(img, p0, tcolor, fcolor)
end subroutine floodfill
end module RCImageArea

View file

@ -0,0 +1 @@
call floodfill(animage, point(100,100), rgb(0,0,0), rgb(0,255,0))

View file

@ -0,0 +1,17 @@
package raster
func (b *Bitmap) Flood(x, y int, repl Pixel) {
targ, _ := b.GetPx(x, y)
var ff func(x, y int)
ff = func(x, y int) {
p, ok := b.GetPx(x, y)
if ok && p.R == targ.R && p.G == targ.G && p.B == targ.B {
b.SetPx(x, y, repl)
ff(x-1, y)
ff(x+1, y)
ff(x, y-1)
ff(x, y+1)
}
}
ff(x, y)
}

View file

@ -0,0 +1,20 @@
package main
import (
"fmt"
"raster"
)
func main() {
b, err := raster.ReadPpmFile("Unfilledcirc.ppm")
if err != nil {
fmt.Println(err)
return
}
b.Flood(200, 200, raster.Pixel{127, 0, 0})
err = b.WritePpmFile("flood.ppm")
if err != nil {
fmt.Println(err)
return
}
}

View file

@ -0,0 +1,110 @@
import Data.Array.ST
import Data.STRef
import Control.Monad
import Control.Monad.ST
import Bitmap
-- Implementation of a stack in the ST monad
pushST :: STStack s a -> a -> ST s ()
pushST s e = do
s2 <- readSTRef s
writeSTRef s (e : s2)
popST :: STStack s a -> ST s (Stack a)
popST s = do
s2 <- readSTRef s
writeSTRef s $ tail s2
return $ take 1 s2
isNotEmptySTStack :: STStack s a -> ST s Bool
isNotEmptySTStack s = do
s2 <- readSTRef s
return $ not $ null s2
emptySTStack :: ST s (STStack s a)
emptySTStack = newSTRef []
consumeSTStack :: STStack s a -> (a -> ST s ()) -> ST s ()
consumeSTStack s f = do
check <- isNotEmptySTStack s
when check $ do
e <- popST s
f $ head e
consumeSTStack s f
type Spanning s = STRef s (Bool, Bool)
setSpanLeft :: Spanning s -> Bool -> ST s ()
setSpanLeft s v = do
(_, r) <- readSTRef s
writeSTRef s (v, r)
setSpanRight :: Spanning s -> Bool -> ST s ()
setSpanRight s v = do
(l, _) <- readSTRef s
writeSTRef s (l, v)
setSpanNone :: Spanning s -> ST s ()
setSpanNone s = writeSTRef s (False, False)
floodFillScanlineStack :: Color c => Image s c -> Pixel -> c -> ST s (Image s c)
floodFillScanlineStack b coords newC = do
stack <- emptySTStack -- new empty stack holding pixels to fill
spans <- newSTRef (False, False) -- keep track of spans in scanWhileX
fFSS b stack coords newC spans -- function loop
return b
where
fFSS b st c newC p = do
oldC <- getPix b c
unless (oldC == newC) $ do
pushST st c -- store the coordinates in the stack
(w, h) <- dimensions b
consumeSTStack st (scanWhileY b p oldC >=>
scanWhileX b st p oldC newC (w, h))
-- take a buffer, the span record, the color of the Color the fill is
-- started from, a coordinate from the stack, and returns the coord
-- of the next point to be filled in the same column
scanWhileY b p oldC coords@(Pixel (x, y)) =
if y >= 0
then do
z <- getPix b coords
if z == oldC
then scanWhileY b p oldC (Pixel (x, y - 1))
else do
setSpanNone p
return (Pixel (x, y + 1))
else do
setSpanNone p
return (Pixel (x, y + 1))
-- take a buffer, a stack, a span record, the old and new color, the
-- height and width of the buffer, and a coordinate.
-- paint the point with the new color, check if the fill must expand
-- to the left or right or both, and store those coordinates in the
-- stack for subsequent filling
scanWhileX b st p oldC newC (w, h) coords@(Pixel (x, y)) =
when (y < h) $ do
z <- getPix b coords
when (z == oldC) $ do
setPix b coords newC
(spanLeft, spanRight) <- readSTRef p
when (not spanLeft && x > 0) $ do
z2 <- getPix b (Pixel (x - 1, y))
when (z2 == oldC) $ do
pushST st (Pixel (x - 1, y))
setSpanLeft p True
when (spanLeft && x > 0) $ do
z3 <- getPix b (Pixel (x - 1, y))
when (z3 /= oldC) $
setSpanLeft p False
when (not spanRight && x < (w - 1)) $ do
z4 <- getPix b (Pixel (x + 1, y))
when (z4 == oldC) $ do
pushST st (Pixel (x + 1, y))
setSpanRight p True
when (spanRight && x < (w - 1)) $ do
z5 <- getPix b (Pixel (x + 1, y))
when (z5 /= oldC) $
setSpanRight p False
scanWhileX b st p oldC newC (w, h) (Pixel (x, y + 1))

View file

@ -0,0 +1,42 @@
import java.awt.Color;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.Deque;
import java.util.LinkedList;
public class FloodFill {
public void floodFill(BufferedImage image, Point node, Color targetColor, Color replacementColor) {
int width = image.getWidth();
int height = image.getHeight();
int target = targetColor.getRGB();
int replacement = replacementColor.getRGB();
if (target != replacement) {
Deque<Point> queue = new LinkedList<Point>();
do {
int x = node.x;
int y = node.y;
while (x > 0 && image.getRGB(x - 1, y) == target) {
x--;
}
boolean spanUp = false;
boolean spanDown = false;
while (x < width && image.getRGB(x, y) == target) {
image.setRGB(x, y, replacement);
if (!spanUp && y > 0 && image.getRGB(x, y - 1) == target) {
queue.add(new Point(x, y - 1));
spanUp = true;
} else if (spanUp && y > 0 && image.getRGB(x, y - 1) != target) {
spanUp = false;
}
if (!spanDown && y < height - 1 && image.getRGB(x, y + 1) == target) {
queue.add(new Point(x, y + 1));
spanDown = true;
} else if (spanDown && y < height - 1 && image.getRGB(x, y + 1) != target) {
spanDown = false;
}
x++;
}
} while ((node = queue.pollFirst()) != null);
}
}
}

View file

@ -0,0 +1,18 @@
import java.io.IOException;
import java.awt.Color;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class Test {
public Test() throws IOException {
BufferedImage image = ImageIO.read(new File("Unfilledcirc.png"));
new FloodFill().floodFill(image, new Point(50, 50), Color.WHITE, Color.RED);
ImageIO.write(image, "png", new File("output.png"));
}
public static void main(String[] args) throws IOException {
new Test();
}
}

View file

@ -0,0 +1,10 @@
#! /usr/bin/perl
use strict;
use Image::Imlib2;
my $img = Image::Imlib2->load("Unfilledcirc.jpg");
$img->set_color(0, 255, 0, 255);
$img->fill(100,100);
$img->save("filledcirc.jpg");
exit 0;

View file

@ -0,0 +1,48 @@
use strict;
use Image::Imlib2;
sub colordistance
{
my ( $c1, $c2 ) = @_;
my ( $r1, $g1, $b1 ) = @$c1;
my ( $r2, $g2, $b2 ) = @$c2;
return sqrt(( ($r1-$r2)**2 + ($g1-$g2)**2 + ($b1-$b2)**2 ))/(255.0*sqrt(3.0));
}
sub floodfill
{
my ( $img, $x, $y, $r, $g, $b ) = @_;
my $distparameter = 0.2;
my %visited = ();
my @queue = ();
my @tcol = ( $r, $g, $b );
my @col = $img->query_pixel($x, $y);
if ( colordistance(\@tcol, \@col) > $distparameter ) { return; }
push @queue, [$x, $y];
while ( @queue ) {
my $pointref = shift(@queue);
( $x, $y ) = @$pointref;
if ( ($x < 0) || ($y < 0) || ( $x >= $img->width ) || ( $y >= $img->height ) ) { next; }
if ( ! exists($visited{"$x,$y"}) ) {
@col = $img->query_pixel($x, $y);
if ( colordistance(\@tcol, \@col) <= $distparameter ) {
$img->draw_point($x, $y);
$visited{"$x,$y"} = 1;
push @queue, [$x+1, $y];
push @queue, [$x-1, $y];
push @queue, [$x, $y+1];
push @queue, [$x, $y-1];
}
}
}
}
# usage example
my $img = Image::Imlib2->load("Unfilledcirc.jpg");
$img->set_color(0,255,0,255);
floodfill($img, 100,100, 0, 0, 0);
$img->save("filledcirc1.jpg");
exit 0;

View file

@ -0,0 +1,10 @@
(de ppmFloodFill (Ppm X Y Color)
(let Target (get Ppm Y X)
(recur (X Y)
(when (= Target (get Ppm Y X))
(set (nth Ppm Y X) Color)
(recurse (dec X) Y)
(recurse (inc X) Y)
(recurse X (dec Y))
(recurse X (inc Y)) ) ) )
Ppm )

View file

@ -0,0 +1,44 @@
import Image
def FloodFill( fileName, initNode, targetColor, replaceColor ):
img = Image.open( fileName )
pix = img.load()
xsize, ysize = img.size
Q = []
if pix[ initNode[0], initNode[1] ] != targetColor:
return img
Q.append( initNode )
while Q != []:
node = Q.pop(0)
if pix[ node[0], node[1] ] == targetColor:
W = list( node )
if node[0] + 1 < xsize:
E = list( [ node[0] + 1, node[1] ] )
else:
E = list( node )
# Move west until color of node does not match targetColor
while pix[ W[0], W[1] ] == targetColor:
pix[ W[0], W[1] ] = replaceColor
if W[1] + 1 < ysize:
if pix[ W[0], W[1] + 1 ] == targetColor:
Q.append( [ W[0], W[1] + 1 ] )
if W[1] - 1 >= 0:
if pix[ W[0], W[1] - 1 ] == targetColor:
Q.append( [ W[0], W[1] - 1 ] )
if W[0] - 1 >= 0:
W[0] = W[0] - 1
else:
break
# Move east until color of node does not match targetColor
while pix[ E[0], E[1] ] == targetColor:
pix[ E[0], E[1] ] = replaceColor
if E[1] + 1 < ysize:
if pix[ E[0], E[1] + 1 ] == targetColor:
Q.append( [ E[0], E[1] + 1 ] )
if E[1] - 1 >= 0:
if pix[ E[0], E[1] - 1 ] == targetColor:
Q.append( [ E[0], E[1] -1 ] )
if E[0] + 1 < xsize:
E[0] = E[0] + 1
else:
break
return img

View file

@ -0,0 +1,7 @@
# "FloodFillClean.png" is name of input file
# [55,55] the x,y coordinate where fill starts
# (0,0,0,255) the target colour being filled( black in this example )
# (255,255,255,255) the final colour ( white in this case )
img = FloodFill( "FloodFillClean.png", [55,55], (0,0,0,255), (255,255,255,255) )
#The resulting image is saved as Filled.png
img.save( "Filled.png" )

View file

@ -0,0 +1,82 @@
#lang racket
(require racket/draw)
;; flood-fill: bitmap<%> number number color color -> void
;; An example of flood filling a bitmap.
;;
;; We'll use a raw, byte-oriented interface here for demonstration
;; purposes. Racket does provide get-pixel and set-pixel functions
;; which work on color% structures rather than bytes, but it's useful
;; to see that the byte approach works as well.
(define (flood-fill bm start-x start-y target-color replacement-color)
;; The main loop.
;; http://en.wikipedia.org/wiki/Flood_fill
(define (iter x y)
(when (and (in-bounds? x y) (target-color-at? x y))
(replace-color-at! x y)
(iter (add1 x) y)
(iter (sub1 x) y)
(iter x (add1 y))
(iter x (sub1 y))))
;; With auxillary definitions below:
(define width (send bm get-width))
(define height (send bm get-height))
(define buffer (make-bytes (* width height 4)))
(send bm get-argb-pixels 0 0 width height buffer)
(define-values (target-red target-green target-blue)
(values (send target-color red)
(send target-color green)
(send target-color blue)))
(define-values (replacement-red replacement-green replacement-blue)
(values (send replacement-color red)
(send replacement-color green)
(send replacement-color blue)))
(define (offset-at x y) (* 4 (+ (* y width) x)))
(define (target-color-at? x y)
(define offset (offset-at x y))
(and (= (bytes-ref buffer (+ offset 1)) target-red)
(= (bytes-ref buffer (+ offset 2)) target-green)
(= (bytes-ref buffer (+ offset 3)) target-blue)))
(define (replace-color-at! x y)
(define offset (offset-at x y))
(bytes-set! buffer (+ offset 1) replacement-red)
(bytes-set! buffer (+ offset 2) replacement-green)
(bytes-set! buffer (+ offset 3) replacement-blue))
(define (in-bounds? x y)
(and (<= 0 x) (< x width) (<= 0 y) (< y height)))
;; Finally, let's do the fill, and then store the
;; result back into the bitmap:
(iter start-x start-y)
(send bm set-argb-pixels 0 0 width height buffer))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Example: flood fill a hole shape.
(define bm (make-bitmap 100 100))
(define dc (send bm make-dc))
;; We intentionally set the smoothing of the dc to
;; aligned so that there are no gaps in the shape for the
;; flood to leak through.
(send dc set-smoothing 'aligned)
(send dc draw-rectangle 10 10 80 80)
(send dc draw-rounded-rectangle 20 20 50 50)
;; In DrRacket, we can print the bm to look at it graphically,
;; before the flood fill:
bm
(flood-fill bm 50 50
(send the-color-database find-color "white")
(send the-color-database find-color "DarkSeaGreen"))
;; ... and after:
bm

View file

@ -0,0 +1,57 @@
class RGBColour
def ==(a_colour)
values == a_colour.values
end
end
class Queue < Array
alias_method :enqueue, :push
alias_method :dequeue, :shift
end
class Pixmap
def flood_fill(pixel, new_colour)
current_colour = self[pixel.x, pixel.y]
queue = Queue.new
queue.enqueue(pixel)
until queue.empty?
p = queue.dequeue
if self[p.x, p.y] == current_colour
west = find_border(p, current_colour, :west)
east = find_border(p, current_colour, :east)
draw_line(west, east, new_colour)
q = west
while q.x <= east.x
[:north, :south].each do |direction|
n = neighbour(q, direction)
queue.enqueue(n) if self[n.x, n.y] == current_colour
end
q = neighbour(q, :east)
end
end
end
end
def neighbour(pixel, direction)
case direction
when :north then Pixel[pixel.x, pixel.y - 1]
when :south then Pixel[pixel.x, pixel.y + 1]
when :east then Pixel[pixel.x + 1, pixel.y]
when :west then Pixel[pixel.x - 1, pixel.y]
end
end
def find_border(pixel, colour, direction)
nextp = neighbour(pixel, direction)
while self[nextp.x, nextp.y] == colour
pixel = nextp
nextp = neighbour(pixel, direction)
end
pixel
end
end
bitmap = Pixmap.new(300, 300)
bitmap.draw_circle(Pixel[149,149], 120, RGBColour::BLACK)
bitmap.draw_circle(Pixel[200,100], 40, RGBColour::BLACK)
bitmap.flood_fill(Pixel[140,160], RGBColour::BLUE)

View file

@ -0,0 +1,74 @@
package require Tcl 8.5
package require Tk
package require struct::queue
proc floodFill {img colour point} {
set new [colour2rgb $colour]
set old [getPixel $img $point]
struct::queue Q
Q put $point
while {[Q size] > 0} {
set p [Q get]
if {[getPixel $img $p] eq $old} {
set w [findBorder $img $p $old west]
set e [findBorder $img $p $old east]
drawLine $img $new $w $e
set q $w
while {[x $q] <= [x $e]} {
set n [neighbour $q north]
if {[getPixel $img $n] eq $old} {Q put $n}
set s [neighbour $q south]
if {[getPixel $img $s] eq $old} {Q put $s}
set q [neighbour $q east]
}
}
}
Q destroy
}
proc findBorder {img p colour dir} {
set lookahead [neighbour $p $dir]
while {[getPixel $img $lookahead] eq $colour} {
set p $lookahead
set lookahead [neighbour $p $dir]
}
return $p
}
proc x p {lindex $p 0}
proc y p {lindex $p 1}
proc neighbour {p dir} {
lassign $p x y
switch -exact -- $dir {
west {return [list [incr x -1] $y]}
east {return [list [incr x] $y]}
north {return [list $x [incr y -1]]}
south {return [list $x [incr y]]}
}
}
proc colour2rgb {color_name} {
foreach part [winfo rgb . $color_name] {
append colour [format %02x [expr {$part >> 8}]]
}
return #$colour
}
set img [newImage 70 50]
fill $img white
drawLine $img blue {0 0} {0 25}
drawLine $img blue {0 25} {35 25}
drawLine $img blue {35 25} {35 0}
drawLine $img blue {35 0} {0 0}
floodFill $img yellow {3 3}
drawCircle $img black {35 25} 24
drawCircle $img black {35 25} 10
floodFill $img orange {34 5}
floodFill $img red {36 5}
toplevel .flood
label .flood.l -image $img
pack .flood.l