是否有适当的算法来检测图形的背景颜色?

对于大学,我们已经给出了一个任务,在给定图像的情况下,我们必须识别“数字”,它们的颜色以及它们内部的“像素组”的数量。 让我解释:

在此处输入图像描述

上面的图像有一个图形 (在图像中可以有多个图形,但让我们暂时忘记它)。

  • canvas的背景颜色是0,0处的像素(在本例中为黄色)
  • 图形的边框颜色为黑色(可以是除canvas背景颜色以外的任何颜色)。
  • 图的背景颜色为白色(也可以与canvas的背景颜色相同)。
  • 图形只能有一种背景颜色。
  • 图中有两个像素组。 一个是蓝色像素池,另一个是红色池,里面有一些绿色。 如您所见,像素组像素的颜色无关紧要(它与图形的背景颜色不同)。 重要的是他们有联系(甚至是对角线)。 因此,尽管有两种不同的颜色,但无论如何这种组被认为只是一种。
  • 如您所见,边界可以像您希望的那样不规则。 然而,它只有一种颜色。
  • 众所周知,像素组不会触及边界。
  • 我被告知像素组的颜色可以是除了图形背景颜色之外的任何颜色。 我假设它可以与图的边框颜色(黑色)相同。

我们已经给出了一个能够拍摄图像并将它们转换成矩阵的类(每个元素是表示像素颜色的整数)。

就是这样。 我是用Java做的。

我做了什么

  • 迭代矩阵中的每个像素
  • 如果我发现一个与背景颜色不同的像素,我会认为它属于图的边界。 从现在开始我将这个像素称为initialPixel
  • 请注意,我提供的图像中的initialPixel是图中左上角的黑色像素。 我有目的地在那里做了一个尖锐的切口来说明它。
  • 我现在的任务是找到图形的背景颜色(在这种情况下是白色)。

但是我找到这样的背景颜色(白色)会遇到很多麻烦。 这是我所做的最接近的方法,适用于某些情况 – 但不适用于此图像:

  • 由于我知道边框的颜色,我可以在initialPixel南边找到第一种不同的颜色。 听起来好像是个好主意 – 它确实有效,但它不适用于所提供的图像:在这种情况下它会返回黄色,因为initialPixel远离图形的内容。

假设我确实找到了图形的背景颜色(白色),我的下一个任务就是要意识到图中存在两个像素组。 这个似乎更容易:

  • 由于我现在知道图形的背景颜色(白色),我可以尝试迭代图中的每个像素,如果我发现一个不属于边框并且不是图形背景的一部分,我已经可以告诉它有一个像素组。 我可以开始递归函数来找到与这样的组相关的所有像素并“标记”它们,以便在将来的迭代中我可以完全忽略这些像素。

我需要的

是的,我的问题是关于如何找到图形的背景颜色(请记住它可以与整个图像的背景颜色相同 – 现在它是黄色的,但它也可以是白色的)基于我之前描述的内容。

我不需要任何代码 – 我只是在为这样的代码思考一个合适的算法时遇到了麻烦。 边界可能有如此奇怪的不规则线条的事实正在扼杀我。

甚至更好:我一直都做错了吗? 也许我不应该那么关注那个initialPixel 。 也许一种不同的初始方法会起作用? 是否有关于此类主题的文档/示例? 我意识到有很多关于“计算机视觉”等的研究,但我对这个特定的问题找不到多少。

一些代码

我的函数检索带有所有数字的向量:*注意: Figure只是一个包含一些值的类,如背景颜色和元素数。

 public Figure[] getFiguresFromImage(Image image) { Figure[] tempFigures = new Figure[100]; int numberOfFigures = 0; matrixOfImage = image.getMatrix(); int imageBackgroundColor = matrixOfImage[0][0]; int pixel = 0; for (int y = 0; y < matrixOfImage.length; ++y) { for (int x = 0; x < matrixOfImage[0].length; ++x) { pixel = matrixOfImage[y][x]; if (!exploredPixels[y][x]) { // This pixel has not been evaluated yet if (pixel != imageBackgroundColor ) { // This pixel is different than the background color // Since it is a new pixel, I assume it is the initial pixel of a new figure // Get the figure based on the initial pixel found tempFigures[numberOfFigures] = retrieveFigure(y,x); ++numberOfFigures; } } } } // ** Do some work here after getting my figures ** return null; } 

然后,很明显,函数retrieveFigure(y,x)是我无法做到的。

笔记:

  • 出于学习目的,我不应该使用任何外部库。

解决此问题的一个好方法是将图像视为图形 ,其中每个颜色填充区域都有一个节点(此答案中的“组件”)。

以下是实现此方法的一种方法:

  1. 将所有像素标记为未访问。
  2. 对于每个像素,如果未访问该像素,则对其执行泛光填充算法。 在泛洪填充标记期间,每个连接的像素都被访问。

    现在,您应该在图像(或“组件”)中有一个纯色区域列表,因此您只需弄清楚它们是如何相互连接的:

  3. 找到与背景颜色分量相邻的像素的组件 – 这是您的图形边框。 请注意,您可以通过查找具有0,0像素的组件来查找背景颜色分量。

  4. 现在找到与新找到的“图形边框”组件相邻的像素的组件。 将有两个这样的组件 – 选择一个不是背景的组件(即没有0​​,0像素)。 这是你的数字背景。

  5. 要查找像素组,只需计算与图形背景组件相邻的像素数量(当然忽略图形边框组件)

这种方法的优点:

  • 以O(#pixels)时间运行。
  • 易于理解和实施。
  • 不假设背景颜色和图形背景颜色不同。

为了确保您了解如何迭代组件及其邻居可能如何工作,这是步骤5的示例伪代码实现:

 List allComponents; // created in step 2 Component background; // found in step 3 (this is the component with the 0,0 pixel) Component figureBorder; // found in step 4 List pixelGroups = new List(); // list of pixel groups for each Component c in allComponents: if c == background: continue; for each Pixel pixel in c.pixelList: for each Pixel neighbor in pixel.neighbors: if neighbor.getComponent() == figureBorder: c.isPixelGroup = true; int numPixelGroups = 0; for each Component c in allComponents: if (c.isPixelGroup) numPixelGroups++; 

试试这段代码:

 import java.util.Scanner; import java.awt.image.BufferedImage; import java.io.*; import javax.imageio.ImageIO; class Analyzer{ private int pixdata[][]; private int rgbdata[][]; private BufferedImage image; int background_color; int border_color; int imagebg_color; private void populateRGB(){ rgbdata = new int[image.getWidth()][image.getHeight()]; for(int i = 0; i < image.getWidth(); i++){ for(int j = 0; j < image.getHeight(); j++){ rgbdata[i][j] = image.getRGB(i, j); } } int howmanydone = 0; int prevcolor,newcolor; prevcolor = rgbdata[0][0]; /* for(int i = 0; i < image.getWidth(); i++){ for(int j = 0; j < image.getHeight(); j++){ System.out.print(rgbdata[i][j]); } System.out.println(""); }*/ for(int i = 0; i < image.getWidth(); i++){ for(int j = 0; j < image.getHeight(); j++){ newcolor = rgbdata[i][j]; if((howmanydone == 0) && (newcolor != prevcolor)){ background_color = prevcolor; border_color = newcolor; prevcolor = newcolor; howmanydone = 1; } if((newcolor != prevcolor) && (howmanydone == 1)){ imagebg_color = newcolor; } } } } public Analyzer(){ background_color = 0; border_color = 0; imagebg_color = 0;} public int background(){ return background_color; } public int border() { return border_color;} public int imagebg() {return imagebg_color;} public int analyze(String filename,String what) throws IOException{ image = ImageIO.read(new File(filename)); pixdata = new int[image.getHeight()][image.getWidth()]; populateRGB(); if(what.equals("background"))return background(); if(what.equals("border"))return border(); if(what.equals("image-background"))return imagebg(); else return 0; } } public class ImageAnalyze{ public static void main(String[] args){ Analyzer an = new Analyzer(); String imageName; Scanner scan = new Scanner(System.in); System.out.print("Enter image name:"); imageName = scan.nextLine(); try{ int a = an.analyze(imageName,"border");//"border","image-background","background" will get you different colors System.out.printf("Color bg: %x",a); }catch(Exception e){ System.out.println(e.getMessage()); } } } 

返回的颜色是ARGB格式。 您需要从中提取R,G和B.

这段代码中有一个错误。 使用有限状态机进行实施。 在第一个状态你是在图像里面,因此0,0是背景颜色,然后当有变化时,变化是边框颜色,然后第三个状态是在图像内部+边框内部和颜色变化。