我不明白为什么我的C++代码运行得如此缓慢。

3
我正在进行Sobel掩模边缘检测,但不使用任何特殊库。我想得到的输出是一个512x512矩阵的文本文件,其值介于0到1之间。 我已经通过使用较小的值(如50)替换'ROW-2'和'COL-2'来验证代码是否工作。 然而,如果我将它们放回去,代码需要很长时间才能运行。
常量值为:
const int ROW = 512;
const int COL = 512;
const double Gx [3][3] = { {-1.0,0.0,1.0},{-2.0,0.0,2.0},{-1.0,0.0,1.0}};
const double Gy [3][3] = { {1.0,2.0,1.0},{0.0,0.0,0.0},{-1.0,-2.0,-1.0}};

这是主要函数:

int main()

{  
    double NewImage[ROW][COL] = {0};    

    for (int i = 0; i < ROW; i++)
    {
        for (int j = 0; j < COL; j++)
        {
            NewImage[i][j] = 0;
        }
    }

    for (int i = 0; i < ROW-2; i++)
    {
        for (int j = 0; j < COL-2; j++)
        {

            NewImage[i+1][j+1] = SobelConvolution(i,j); 
        }
    }

    ofstream newImage;
    string filename;
    filename = "output image.txt";

    newImage.open (filename.c_str());

    for(int rows = 0; rows < ROW; rows++)
    {
        for(int cols = 0; cols < COL; cols++)
        {
            newImage << NewImage[ROW][COL] <<" ";
        }
        newImage << endl;
    }

    newImage.close();

    return 0;
}

这是SobelConvolution函数的定义:
double SobelConvolution(int row, int col)
{   
    double convX;
    double convY;
    double conv;

    convX = ImageReader(row,col)*Gx[2][2]
            + ImageReader(row,col+1)*Gx[2][1]
            + ImageReader(row,col+2)*Gx[2][0]
            + ImageReader(row+1,col)*Gx[1][2]
            + ImageReader(row+1,col+1)*Gx[1][1]
            + ImageReader(row+1,col+2)*Gx[1][0]
            + ImageReader(row+2,col)*Gx[0][2]
            + ImageReader(row+2,col+1)*Gx[0][1]
            + ImageReader(row+2,col+2)*Gx[0][0];

    convY = ImageReader(row,col)*Gy[2][2]
            + ImageReader(row,col+1)*Gy[2][1]
            + ImageReader(row,col+2)*Gy[2][0]
            + ImageReader(row+1,col)*Gy[1][2]
            + ImageReader(row+1,col+1)*Gy[1][1]
            + ImageReader(row+1,col+2)*Gy[1][0]
            + ImageReader(row+2,col)*Gy[0][2]
            + ImageReader(row+2,col+1)*Gy[0][1]
            + ImageReader(row+2,col+2)*Gy[0][0];

    conv = sqrt((convX*convX) + (convY*convY));


    return conv;
}

这是ImageReader函数:

double ImageReader(int r, int c)
{
    double OrigImage[ROW][COL];

    ifstream defaultImage ("image.txt");

    if (defaultImage.good())
    {
        for (int i = 0; i < ROW; i++)
        {
            for (int j = 0; j < COL; j++)
            {
                defaultImage >> OrigImage[i][j];
            }
        }
    }
    return OrigImage [r][c]; 
}

有什么提示或建议吗?提前感谢!


你是有意忽略 NewImage 的第一个和最后一个值(只将它们保留为 0)吗? - Rakete1111
是的,我以为这样会简单一些,不用担心边缘上的值.. 这就是程序出问题的原因吗? - Ryan Kim
不好意思 :) 有太多不必要的循环了。;) - Rakete1111
如果您没有启用优化编译,建议使用例如clang和gcc编译器的“-O2”或“-O3”选项。 - Jesper Juhl
3个回答

4
这里有一些笔记:
  • ImageReader

    仅返回数组的一个值,每次只需要单个值时无需读取整个数组。在我看来,此函数是多余的。

  • SobelConvolution

    这个函数不错,但有一个不必要的变量conv

  • main

    我不知道为什么你要将NewImage的每一个值都初始化为0,因为它们已经是0了!你也不实际需要NewImage

以下是我的写作(带有详细说明):

double SobelConvolution(int row, int col)
{
    //ImageReader has been removed, it was unnecessary. The code has been moved here
    double oldImage[ROW][COL];
    std::ifstream defaultImage{ "image.txt" };

    //Error handling if file doesn't exist - consider doing something else :)
    if (!defaultImage.is_open())
        return 0;

    //Initialize array
    for (int i = 0; i < ROW; ++i)
        for (int j = 0; j < COL; ++j)
            defaultImage >> oldImage[i][j];

    //You should always declare variables where they are first used, this
    //reduces the possibility of errors
    //We can just access the array directly
    double convX = oldImage[row][col] * Gx[2][2]
        + oldImage[row][col + 1] * Gx[2][1]
        + oldImage[row][col + 2] * Gx[2][0]
        + oldImage[row + 1][col] * Gx[1][2]
        + oldImage[row + 1][col + 1] * Gx[1][1]
        + oldImage[row + 1][col + 2] * Gx[1][0]
        + oldImage[row + 2][col] * Gx[0][2]
        + oldImage[row + 2][col + 1] * Gx[0][1]
        + oldImage[row + 2][col + 2] * Gx[0][0];

    double convY = oldImage[row][col] * Gy[2][2]
        + oldImage[row][col + 1] * Gy[2][1]
        + oldImage[row][col + 2] * Gy[2][0]
        + oldImage[row + 1][col] * Gy[1][2]
        + oldImage[row + 1][col + 1] * Gy[1][1]
        + oldImage[row + 1][col + 2] * Gy[1][0]
        + oldImage[row + 2][col] * Gy[0][2]
        + oldImage[row + 2][col + 1] *Gy[0][1]
        + oldImage[row + 2][col + 2]*Gy[0][0];

    //No need to create a separate variable just to return it
    return sqrt((convX*convX) + (convY*convY));
}


int main()
{
    //= {} Initializes every element to 0, you don't need to do it :) Just so you know :)
    //Note that it crashes here, because my stack size was too small,
    //maybe consider using a dynamic array (512 * 512 is pretty big) :)
    //double NewImage[ROW][COL] = {};
    //The array is not really needed, see below

    std::string filename = "oimage.txt";
    std::ofstream newImage{ filename };

    //No need to create another array just to output it again,
    //Just output the calculated values - this doesn't ignore the first/last values
    for (int rows = 0; rows < ROW; rows++)
    {
        for (int cols = 0; cols < COL; cols++)
            newImage << SobelConvolution(rows, cols) << " ";
        newImage << '\n'; //std::endl flushes the stream, while \n does not - it is faster :)
    }

    newImage.close();

    return 0;
}

我花了一点时间来看你的评论。谢谢!它真的帮了我很多。我对以那种方式声明数组并不完全确定,所以我添加了更多的变量来澄清,这使得它过于冗余。 - Ryan Kim
@RyanKim 不客气,如果你有更多问题,随时问我 :) - Rakete1111

2
你所做的不仅效率低下,而且非常疯狂。对于每个像素,你都调用SobelConvolution函数,该函数又会调用ImageReader函数18次(其中6次是无用的,因为相应的系数为零)。但可怕的是,ImageReader函数每次都会从文本文件中进行完整的图像读取,而一个简单的数组查找就足够了。
因此,总共进行了4718592次文件流的打开/关闭操作和1236950581248次从文件中读取值的操作,而只需要进行1次打开/关闭操作和262144次读取操作即可。(不包括单个读取比直接访问数组更昂贵的事实。)一次完整的运行可能需要两个小时或更长时间。

2

您真的想打开单个图像文件18次,并读取每行和每列的所有数据,只为了返回18次单个行和列吗?为什么不只读取一次图像文件,然后将图像数据数组传递给函数呢?


哦,我只是把它想象成一个值,忘记了每次这样做时实际上我都在打开整个图像文件.. 谢谢! - Ryan Kim
@RyanKim 而且你每次都在遍历整个数组 :) - Rakete1111

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接