图像处理------泛洪填充算法(Flood Fill Algorithm) 油漆桶功能
泛洪填充算法(Flood Fill Algorithm)
泛洪填充算法又称洪水填充算法是在很多图形绘制软件中常用的填充算法,最熟悉不过就是
windows paint的油漆桶功能。算法的原理很简单,就是从一个点开始附近像素点,填充成新
的颜色,直到封闭区域内的所有像素点都被填充新颜色为止。泛红填充实现最常见有四邻域
像素填充法,八邻域像素填充法,基于扫描线的像素填充方法。根据实现又可以分为递归与
非递归(基于栈)。
在介绍算法的三种实现方式之前,首先来看一下测试该算法的UI实现。基本思路是选择一
张要填充的图片,鼠标点击待填充的区域内部,算法会自动填充该区域,然后UI刷新。完
整的UI代码如下:
- package com.gloomyfish.paint.fill;
- import java.awt.Color;
- import java.awt.Dimension;
- import java.awt.Graphics;
- import java.awt.Graphics2D;
- import java.awt.MediaTracker;
- import java.awt.event.MouseEvent;
- import java.awt.event.MouseListener;
- import java.awt.image.BufferedImage;
- import java.io.File;
- import java.io.IOException;
- import javax.imageio.ImageIO;
- import javax.swing.JComponent;
- import javax.swing.JFileChooser;
- import javax.swing.JFrame;
- public class FloodFillUI extends JComponent implements MouseListener{
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- private BufferedImage rawImg;
- private MediaTracker tracker;
- private Dimension mySize;
- FloodFillAlgorithm ffa;
- public FloodFillUI(File f)
- {
- try {
- rawImg = ImageIO.read(f);
- } catch (IOException e1) {
- e1.printStackTrace();
- }
- tracker = new MediaTracker(this);
- tracker.addImage(rawImg, 1);
- // blocked 10 seconds to load the image data
- try {
- if (!tracker.waitForID(1, 10000)) {
- System.out.println("Load error.");
- System.exit(1);
- }// end if
- } catch (InterruptedException e) {
- e.printStackTrace();
- System.exit(1);
- }// end catch
- mySize = new Dimension(300, 300);
- this.addMouseListener(this);
- ffa = new FloodFillAlgorithm(rawImg);
- JFrame imageFrame = new JFrame("Flood File Algorithm Demo - Gloomyfish");
- imageFrame.getContentPane().add(this);
- imageFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- imageFrame.pack();
- imageFrame.setVisible(true);
- }
- public void paint(Graphics g) {
- Graphics2D g2 = (Graphics2D) g;
- g2.drawImage(rawImg, 10, 10, rawImg.getWidth(), rawImg.getHeight(), null);
- }
- public Dimension getPreferredSize() {
- return mySize;
- }
- public Dimension getMinimumSize() {
- return mySize;
- }
- public Dimension getMaximumSize() {
- return mySize;
- }
- public static void main(String[] args) {
- JFileChooser chooser = new JFileChooser();
- chooser.showOpenDialog(null);
- File f = chooser.getSelectedFile();
- new FloodFillUI(f);
- }
- @Override
- public void mouseClicked(MouseEvent e) {
- System.out.println("Mouse Clicked Event!!");
- int x = (int)e.getPoint().getX();
- int y = (int)e.getPoint().getY();
- System.out.println("mouse location x = " + x); // column
- System.out.println("mouse location y = " + y); // row
- System.out.println();
- long startTime = System.nanoTime();
- // ffa.floodFill4(x, y, Color.GREEN.getRGB(), ffa.getColor(x, y));
- // ffa.floodFill8(x, y, Color.GREEN.getRGB(), ffa.getColor(x, y));
- // ffa.floodFillScanLine(x, y, Color.GREEN.getRGB(), ffa.getColor(x, y)); // 13439051
- ffa.floodFillScanLineWithStack(x, y, Color.GREEN.getRGB(), ffa.getColor(x, y)); // - 16660142
- long endTime = System.nanoTime() - startTime;
- System.out.println("run time = " + endTime);
- ffa.updateResult();
- this.repaint();
- }
- @Override
- public void mousePressed(MouseEvent e) {
- // TODO Auto-generated method stub
- }
- @Override
- public void mouseReleased(MouseEvent e) {
- // TODO Auto-generated method stub
- }
- @Override
- public void mouseEntered(MouseEvent e) {
- // TODO Auto-generated method stub
- }
- @Override
- public void mouseExited(MouseEvent e) {
- // TODO Auto-generated method stub
- }
- }
首先介绍四邻域的泛洪填充算法,寻找像素点p(x, y)的上下左右四个临近像素点,如果没有
被填充,则填充它们,并且继续寻找它们的四邻域像素,直到封闭区域完全被新颜色填充。
蓝色方格为四个邻域像素, p(x, y)为当前像素点。
基于递归实现代码很简单:
- public void floodFill4(int x, int y, int newColor, int oldColor)
- {
- if(x >= 0 && x < width && y >= 0 && y < height
- && getColor(x, y) == oldColor && getColor(x, y) != newColor)
- {
- setColor(x, y, newColor); //set color before starting recursion
- floodFill4(x + 1, y, newColor, oldColor);
- floodFill4(x - 1, y, newColor, oldColor);
- floodFill4(x, y + 1, newColor, oldColor);
- floodFill4(x, y - 1, newColor, oldColor);
- }
- }
八邻域的填充算法,则是在四邻域的基础上增加了左上,左下,右上,右下四个相邻像素。
并递归寻找它们的八邻域像素填充,直到区域完全被新颜色填充。
蓝色方格为四个邻域像素,黄色为左上,左下,右上,右下四个像素, p(x, y)为当前像素点。
基于递归实现的代码也很简单:
- public void floodFill8(int x, int y, int newColor, int oldColor)
- {
- if(x >= 0 && x < width && y >= 0 && y < height &&
- getColor(x, y) == oldColor && getColor(x, y) != newColor)
- {
- setColor(x, y, newColor); //set color before starting recursion
- floodFill8(x + 1, y, newColor, oldColor);
- floodFill8(x - 1, y, newColor, oldColor);
- floodFill8(x, y + 1, newColor, oldColor);
- floodFill8(x, y - 1, newColor, oldColor);
- floodFill8(x + 1, y + 1, newColor, oldColor);
- floodFill8(x - 1, y - 1, newColor, oldColor);
- floodFill8(x - 1, y + 1, newColor, oldColor);
- floodFill8(x + 1, y - 1, newColor, oldColor);
- }
- }
基于扫描线实现的泛洪填充算法的主要思想是根据当前输入的点p(x, y),沿y方向分别向上
与向下扫描填充,同时向左p(x-1, y)与向右p(x+1, y)递归寻找新的扫描线,直到递归结束。
代码如下:
- public void floodFillScanLine(int x, int y, int newColor, int oldColor)
- {
- if(oldColor == newColor) return;
- if(getColor(x, y) != oldColor) return;
- int y1;
- //draw current scanline from start position to the top
- y1 = y;
- while(y1 < height && getColor(x, y1) == oldColor)
- {
- setColor(x, y1, newColor);
- y1++;
- }
- //draw current scanline from start position to the bottom
- y1 = y - 1;
- while(y1 >= 0 && getColor(x, y1) == oldColor)
- {
- setColor(x, y1, newColor);
- y1--;
- }
- //test for new scanlines to the left
- y1 = y;
- while(y1 < height && getColor(x, y1) == newColor)
- {
- if(x > 0 && getColor(x - 1, y1) == oldColor)
- {
- floodFillScanLine(x - 1, y1, newColor, oldColor);
- }
- y1++;
- }
- y1 = y - 1;
- while(y1 >= 0 && getColor(x, y1) == newColor)
- {
- if(x > 0 && getColor(x - 1, y1) == oldColor)
- {
- floodFillScanLine(x - 1, y1, newColor, oldColor);
- }
- y1--;
- }
- //test for new scanlines to the right
- y1 = y;
- while(y1 < height && getColor(x, y1) == newColor)
- {
- if(x < width - 1 && getColor(x + 1, y1) == oldColor)
- {
- floodFillScanLine(x + 1, y1, newColor, oldColor);
- }
- y1++;
- }
- y1 = y - 1;
- while(y1 >= 0 && getColor(x, y1) == newColor)
- {
- if(x < width - 1 && getColor(x + 1, y1) == oldColor)
- {
- floodFillScanLine(x + 1, y1, newColor, oldColor);
- }
- y1--;
- }
- }
基于递归实现的泛洪填充算法有个致命的缺点,就是对于大的区域填充时可能导致JAVA栈溢出
错误,对最后一种基于扫描线的算法,实现了一种非递归的泛洪填充算法。
- public void floodFillScanLineWithStack(int x, int y, int newColor, int oldColor)
- {
- if(oldColor == newColor) {
- System.out.println("do nothing !!!, filled area!!");
- return;
- }
- emptyStack();
- int y1;
- boolean spanLeft, spanRight;
- push(x, y);
- while(true)
- {
- x = popx();
- if(x == -1) return;
- y = popy();
- y1 = y;
- while(y1 >= 0 && getColor(x, y1) == oldColor) y1--; // go to line top/bottom
- y1++; // start from line starting point pixel
- spanLeft = spanRight = false;
- while(y1 < height && getColor(x, y1) == oldColor)
- {
- setColor(x, y1, newColor);
- if(!spanLeft && x > 0 && getColor(x - 1, y1) == oldColor)// just keep left line once in the stack
- {
- push(x - 1, y1);
- spanLeft = true;
- }
- else if(spanLeft && x > 0 && getColor(x - 1, y1) != oldColor)
- {
- spanLeft = false;
- }
- if(!spanRight && x < width - 1 && getColor(x + 1, y1) == oldColor) // just keep right line once in the stack
- {
- push(x + 1, y1);
- spanRight = true;
- }
- else if(spanRight && x < width - 1 && getColor(x + 1, y1) != oldColor)
- {
- spanRight = false;
- }
- y1++;
- }
- }
- }
; // will be increased as needed
图像处理------泛洪填充算法(Flood Fill Algorithm) 油漆桶功能的更多相关文章
- 图像处理之泛洪填充算法(Flood Fill Algorithm)
泛洪填充算法(Flood Fill Algorithm) 泛洪填充算法又称洪水填充算法是在很多图形绘制软件中常用的填充算法,最熟悉不过就是 windows paint的油漆桶功能.算法的原理很简单,就 ...
- OpenCV---ROI(region of interest)和泛洪填充
一:ROI 感兴趣区(Region of Interest,ROIs) 是图像的一部分,它通过在图像上选择或使用诸如设定阈值(thresholding) 或者从其他文件(如矢量> 转换获得等方法 ...
- 八 ROI(region of interest)和泛洪填充
一.ROI 感兴趣区(Region of Interest,ROIs) 是图像的一部分,它通过在图像上选择或使用诸如设定阈值(thresholding) 或者从其他文件(如矢量> 转换获得等方法 ...
- Python+OpenCV图像处理(六)—— ROI与泛洪填充
一.ROI ROI(region of interest),感兴趣区域.机器视觉.图像处理中,从被处理的图像以方框.圆.椭圆.不规则多边形等方式勾勒出需要处理的区域,称为感兴趣区域,ROI. 代码如下 ...
- CGA填充算法之种子填充算法
CGA填充算法之种子填充算法 平面区域填充算法是计算机图形学领域的一个很重要的算法,区域填充即给出一个区域的边界 (也可以是没有边界,只是给出指定颜色),要求将边界范围内的所有象素单元都修改成指定的颜 ...
- 【Leetcode_easy】733. Flood Fill
problem 733. Flood Fill 题意:图像处理中的泛洪填充算法,常见的有四邻域像素填充法.八邻域像素填充法.基于扫描线的像素填充法,实现方法分为递归与非递归(基于栈). 泛洪填充算法原 ...
- 和同事谈谈Flood Fill 算法
前言 今天忙完了公司的工作后,发现同事在做LeeCode的算法题,顿时来了兴趣,于是王子与同事一起探讨如何能做好算法题,今天在此文章中和大家分享一下. 什么是Flood Fill 算法 我们今天谈论的 ...
- 带你学习Flood Fill算法与最短路模型
一.Flood Fill(连通块问题) 0.简介 Flood Fill(洪水覆盖) 可以在线性的时间复杂内,找到某个点所在的连通块! 注:基于宽搜的思想,深搜也可以做但可能会爆栈 flood fill ...
- LeetCode算法题-Flood Fill(Java实现)
这是悦乐书的第306次更新,第325篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第173题(顺位题号是733).图像由二维整数数组表示,每个整数表示图像的像素值(从0到 ...
随机推荐
- jdk环境变量配置及配置原因
windows下: 一.设置环境变量 变量名:JAVA_HOME 变量值:D:\Program Files\Java\jdk1.6.0 (即jdk安装的位置) 变量名:Path (这个变量名已在系 ...
- STP协议
STP协议的作用: 第一, 可以避免网络环路和广播风暴. 二层数据包是没有TTL(time to live)的,如果不加以限制,可能会无限的传播下去.二层设备间如果存在环路,大量的广播包就可能导致广播 ...
- IOLI-crackme0x01-0x05 writeup
上一篇开了个头, 使用Radare2并用3中方法来解决crackme0x00, 由于是第一篇, 所以解释得事无巨细, 今天就稍微加快点步伐, 分析一下另外几个crackme. 如果你忘记了crackm ...
- testlink用例的导出到Excel
一直在网上寻找怎么把testlink的用例导出到Excel中,以及把Excel中已经写好的用例导入到Testlink中的方法.根据现网的经验,然后修改了一下.贴出来,以飨有这方面需求的测试同仁. Te ...
- msp430系统时钟
转自 http://m.blog.csdn.net/blog/msdumin/38441493# MSP430基础时钟模块包含以下3个时钟输入源. 一.4个时钟振荡源 1.LFXT1CLK: 外部晶 ...
- Excel地图插件(ExcelMaps)
1 软件概述 一款基于Excel表格进行地理数据挖掘.坐标转换.地图绘制.地图图表等操作的工具类应用软件.具有易用.高效.稳定的特点,能够满足地理数据应用及处理的基本需求,也可提供个性化定制开发服务. ...
- 重写equals()和hashCode()
什么时候需要重写equals()? 只有当一个实例等于它本身的时候,equals()才会返回true值.通俗地说,此时比较的是两个引用是否指向内存中的同一个对象,也可以称做是否实例相 等.而我们在使用 ...
- NSIS 设置系统变量
定义 ; include for some of the windows messages defines !include "winmessages.nsh" ; HKLM (a ...
- Swift百万线程攻破单例(Singleton)模式
一.不安全的单例实现 在上一篇文章我们给出了单例的设计模式,直接给出了线程安全的实现方法.单例的实现有多种方法,如下面: class SwiftSingleton { class var shared ...
- 关于DOM与BOM的总结
1.什么是BOM,什么是DOM(基本概念) BOM: Browers Object MOdel 浏览器对象模型 DOM: Document Object MOdel ...