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.