8

在大学里,我们被分配了一项任务,给定一张图像,我们必须识别“图形”、它们的颜色以及它们内部“像素组”的数量。让我解释:

在此处输入图像描述

上图有一个数字(图中可以有多个数字,但我们暂时先不管它)。

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

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

就是这样。我正在用 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)是我无法做到的。

笔记:

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

2 回答 2

6

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

这是实现此方法的一种方法:

  1. 将所有像素标记为未访问。

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

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

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

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

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

这种方法的优点:

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

为确保您了解迭代组件及其邻居的工作方式,以下是第 5 步的示例伪代码实现:

List<Component> 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<Component> pixelGroups = new List<Component>(); // 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++;
于 2012-10-21T06:30:37.937 回答
1

试试这个代码:

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 是背景颜色,然后当发生变化时,变化是边框颜色,然后第三个状态是在图像内部 + 边框和颜色内部变化。

于 2012-10-21T06:16:29.450 回答