简体   繁体   中英

I don't understand why my c++ code running so slow

I'm working on Sobel masking for edge detection without using any special library. The output that I want to get is a text files with 512x512 matrix with values between 0 to 1. I've checked that the code is working by putting smaller values like 50 instead of 'ROW-2' and 'COL-2'. However if I put them back, the code takes forever to run.

Constant values are:

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}};

This is the main function:

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;
}

This is the function 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;
}

This is the function 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]; 
}

Any hint or advice? Thanks in advance!

Here are some notes:

  • ImageReader

    Returns only one value of the array, no need to read through the whole array every time you just need a single value. In my opinion this function is redundant.

  • SobelConvolution

    This function is good, but there is a unnecessary variable - conv .

  • main

    I have no idea why you are initializing every value of NewImage to 0 , when they are already 0 ! You also don't actually need NewImage

Here's what I would write (with extensive commenting):

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;
}

Do you really mean to open the single image file 18 times and read all the data for each row and column just to return a single row and column 18 times? Why not read the image file once and pass the image data array to the function?

What you are doing is not just a little inefficient, it is -sorry- completely insane.

For every pixel of the image, you call SobelConvolution which in turn calls ImageReader 18 times (of which 6 are of no use as the corresponding coefficient is zero). But the horrendous thing is that ImageReader performs a complete image read every time, from a text file, where a simple array lookup would suffice.

So in total, you are performing 4718592 file stream opening/closing and 1236950581248 value reads from the file, where just 1 opening/closing and 262144 reads would suffice. (Not counting that a single read is much more costly than a straight array access.) A complete run could last like two hours or more.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM