简体   繁体   中英

How to modify a value at a position in cv::Mat class (16UC1)

In order to gain a better understanding of computer vision, I am trying to write a canny edge detection algorithm "from scratch" (not just using the canny function from the cv lib). In order to do this, I need to apply a hysteresis threshold to the image. However, I get an error when I try to modify an element in my matrix. Here is my function:

cv::Mat hysteresisThresh(cv::Mat originalImage, int lowThresh, int highThresh){
    //initialize a 2d vector to keep track of whethr or not the pixel is "in" the image
    std::vector<std::vector<bool>>isIn(
                    originalImage.rows,
                    std::vector<bool>(originalImage.cols, true));
    //loop through every row and col in the original image
    //create a varaible to hold the element being checked
    unsigned short curElementValue;
    for(int curRow = 0; curRow<originalImage.rows; curRow++){
            for(int curCol = 0; curCol<originalImage.cols; curCol++){
                    curElementValue = originalImage.at<unsigned short>(curRow, curCol);
                    if(curElementValue > highThresh){
                            isIn.at(curRow).at(curCol) = true;
                            //do nothing to the returnImage since the correct value is already stored       
                    }
                    else if(curElementValue < highThresh && curElementValue > lowThresh){
                            /* i need to check that this pixel is adjacent to another edge pixel
                            thus, I have 8  possabilities. Think of them as:
                            123
                            4*5
                            678
                            with the * being the current pixel. However, I can cut the possabilities
                            in half since pixels 5-8 (inclusive) have not been checked yet and will be
                            checked in the future. So, I will only check pixels 1-4
                            */
                            //TODO there may be a more efficient way to check these values. Find out        
                            //The first stage of this if is the be sure that you are checking values in the array that actually exist

                            if((curRow!=0 && curCol!=0 && curRow!=originalImage.rows-1 && curCol!=originalImage.cols-1)&&
                            (isIn[curRow][curCol-1] || isIn[curRow-1][curCol-1]|| isIn[curRow-1][curCol] || isIn[curRow-1][curCol+1])){
                            isIn.at(curRow).at(curCol) = true;
                            //do nothing to the returnImage since the correct value is already stored
                            }
                            else{
                            //none of the adjacent pixels are in, so it is not an edge
                            isIn.at(curRow).at(curCol) = false;
                            originalImage.at<unsigned short>(curRow, curCol) = 0; //same as above
                            }

                    }
                    else{

                            isIn.at(curRow).at(curCol) = false;
                            originalImage.at<unsigned short>(curRow, curCol) = 0; //same as above

                    }
            }
    }
    return originalImage;

}

The output upon running the program is a classic seg fault:

Segmentation fault (core dumped)

I've explored in gdb a bit and found that the fault occurs at the line:

originalImage.at<unsigned short>(curRow, curCol) = 0;

What am I doing wrong? Thanks!

EDIT: I was asked in the comments to provide context for the function. This is the code surrounding the function call. I am calling all of the operations on a depth map (yes I am aware canny is designed for use with color maps but after researching I think the same general principles would apply to finding edges in a depth map). Anyway, here is the code:

    void displayDepthEdges(cv::VideoCapture* capture){

    //initialize the matricies for holding images
    cv::Mat depthImage;
    cv::Mat edgeImage;
    cv::Mat bgrImage;
    //initialize variables for sobel operations
    cv::Mat yGradient;
    cv::Mat xGradient;
    //double theta;
    //initialize variable for hysteresis thresh operation
    cv::Mat threshedImage;
    int cannyLowThresh = 0;
    int cannyHighThresh = 500;
    //initialize variables for erosion and dilation based noise cancelation.
    //CURRENTLY NOT IN USE
    //int erosionFactor = 0;
    //int dilationFactor = 0;
    //initialize the windows and trackbars used to control everything
    //cv::namedWindow("EroDilMenu", CV_WINDOW_NORMAL);
    cv::namedWindow("CannyMenu", CV_WINDOW_NORMAL);
    cvCreateTrackbar("Low Thresh", "CannyMenu",&cannyLowThresh, 500);
    cvCreateTrackbar("High Thresh", "CannyMenu",&cannyHighThresh, 500);
    //cvCreateTrackbar("Dil Fac", "EroDilMenu",&dilationFactor, 25);
    //cvCreateTrackbar("Ero Fac", "EroDilMenu", &erosionFactor, 25); 
    cv::namedWindow("Edges", CV_WINDOW_NORMAL);
    for(;;){
            if(!capture->grab()){
                    std::cerr << "Error: Could not grab an image\n";
            }
            else{
                    //initialization here will stop totalGradient from carrying values over (hopefully)
                    cv::Mat totalGradient;
                    capture->retrieve(depthImage, CV_CAP_OPENNI_DEPTH_MAP);

                    //blur the image so that only major edges are detected
                    GaussianBlur(depthImage, depthImage, cv::Size(3,3), 1, 0, cv::BORDER_DEFAULT);
                    //apply the sobel operator in the x and y directions, then average to approximate gradient
                    //arguments are(input, output, dataType, x order derivative, y order derivative
                    //matrix size, scale, delta)
                    Sobel(depthImage, xGradient, CV_16U, 1, 0, 3, 1, 0, cv::BORDER_DEFAULT);
                    convertScaleAbs(xGradient, xGradient);
                    Sobel(depthImage, yGradient, CV_16U, 1, 0, 3, 1, 0, cv::BORDER_DEFAULT);
                    convertScaleAbs(yGradient, yGradient);
                    addWeighted(yGradient, 0.5, xGradient, 0.5, 0.0, totalGradient);
                    //it is not clear that noise canceling does anything here... 
                    //it seemed to only blur or sharpen edges in testing
                    //totalGradient = noiseCancel(totalGradient ,erosionFactor, dilationFactor);



                    //TODO impliment direction based edge priority
                    //theta = atan2(xGradient, yGradient);//this is returned in radians
                    //times 10 since the threshes are in mm and trackbars are in cm
                    totalGradient = hysteresisThresh(totalGradient,cannyLowThresh*10, cannyHighThresh*10);
                    imshow("Edges", totalGradient);
                    if(cv::waitKey(30) == 27){break;}
            }
    }

}

Your mistake is in surrounding code, not in algorithm itself. According to documentation (it's also correct for 3.0.0 version), cv::convertScaleAbs converts result to 8-bit image, ie CV_8UC1 type. As a result your totalGradiend is 8-bit image, and its elements can't be accessed via unsigned short pointer.

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