I am trying to implement a function that erodes a picture without using the built in erosion function in opencv.
My approach is to check if there are any 0's in the image area and if the kernel is one then I can't set the one, which means the condition is false.
Then my second if statement means that the condition is true then my 1 is set .
void erosion(const cv::Mat& image, cv::Mat& erosion_image, const cv::Mat& kernel)
{
bool check;
int count;
int count_2;
int anchorx = kernel.rows / (2.0);
int anchory = kernel.cols / (2.0);
for (int x = 0; x < image.rows; x++) {
for (int y = 0; y < image.cols; y++) {
kernel.at<uchar>(x, y);
for (int count = 0; count < kernel.rows; count++) {
for (int count_2 = 0; count_2 < kernel.cols; count_2++) {
if (image.at<uchar>(x + count, y + count_2) == 0 && kernel.at<uchar>(count, count_2) == 1) {
check = false;
}
}
}
if (check) {
erosion_image.at<uchar>(x, y) = 1;
}
}
}
}
is this the right approach?
thank you in advance
This is define of erosion for binary images. If you process grayscale image you should set a pixel value to minimum value of the points in its neighborhood Wiki-Erosion.
In following code instead of maximum pixel value 1 i used 255 - default for OpenCV maximum value of CV_8U image type.
Note, you also need to choose how to process border pixels of image. In following code they just not processed.
The erosion for binary image can be represented as something like that:
void erosion(const cv::Mat& image, cv::Mat& erosion_image, const cv::Mat& kernel)
{
bool check;
int count;
int count_2;
int anchorx = kernel.rows / (2.0);
int anchory = kernel.cols / (2.0);
for (int x = anchorx; x < image.rows - anchorx; x++) {
for (int y = anchory; y < image.cols - anchory; y++) {
check = true;
if (image.at<uchar>(x, y))
{
for (int count = 0; count < kernel.rows && check; count++) {
for (int count_2 = 0; count_2 < kernel.cols && check; count_2++) {
if (image.at<uchar>(x - anchorx + count, y - anchory + count_2) == 0 &&
kernel.at<uchar>(count, count_2) == 1) {
erosion_image.at<uchar>(x, y) = 0;
check = false;
}
}
}
if (check)
erosion_image.at<uchar>(x, y) = 255;
}
else
erosion_image.at<uchar>(x, y) = 0;
}
}
}
Related
This is the part of code that I've written to build the matrix to calculate mean threshold of specific zone in multiple images.
The problem is that I get the value of mean threshold: -2147483648
(Apologies for language error)
Here is my code:
int moyenne(cv::Mat& image, cv::Point seed) {
double th = 0;
int count = 0;
int N = 3;
for (int x = seed.x - N; x <= seed.x + N; x++) {
for (int y = seed.y - N; y <= seed.y + N; y++) {
if (x == seed.x && y == seed.y) {
continue;
}
else {
int som = 0;
som = +(abs(int(image.at<cv::Vec3b>(seed.x, seed.y)[0])));
if (som == N) {
N++;
}
while (som <N ) {
th += abs(int(image.at<cv::Vec3b>(seed)[0] - image.at<cv::Vec3b>(x, y)[0]));
count++;
}
}
}
} return th / count;
}
I am wanting to move through an image and take a 5x5 grid centered around each pixel in the image. I then want to sum that grid and compare it to a threshold.
int main()
{
Mat element = getStructuringElement(MORPH_RECT, Size(7, 7));
Mat im = imread("blob.png", IMREAD_GRAYSCALE);
bool fromCenter = false;
namedWindow("Crop frame", WINDOW_NORMAL);
Rect2d r = selectROI("Crop frame", im, fromCenter);
im = im(r);
erode(im, im, element);
Mat clone = im;
int sectionSize = 4;
int width = im.cols - sectionSize/2;
int height = im.rows - sectionSize/2;
int sum = 0;
int counter = 0;
for (int i = sectionSize/2; i < width; i++) {
for (int j = sectionSize/2; j < height; j++) {
Rect rect = Rect(i, j, sectionSize, sectionSize);
rect -= Point(rect.width / 2, rect.height / 2);
Mat temp = im(rect);
for (int x = 0; x < temp.cols; x++) {
for (int y = 0; y < temp.rows; y++) {
int pixelValue = (int)temp.at<uchar>(y, x);
sum += pixelValue;
}
}
cout << sum << endl;
if (sum > 3800) {
clone.at<uchar>(j, i) = 255;
}
else {
clone.at<uchar>(j, i) = 0;
}
namedWindow("erode", WINDOW_NORMAL);
imshow("erode", clone);
waitKey(1);
sum = 0;
}
}
}
I am getting fluctuations in the pixel sum based on where I select my ROI in the image even when both over white space Also, my pixel sum is changing when I change the value of the clone pixel in this section of the code which I do not understand at all:
if (sum > 3800) {
clone.at<uchar>(j, i) = 255;
}
else {
clone.at<uchar>(j, i) = 0;
}
I am trying to implement laplacian filter for sharpening an image.
but the result is kinda grey , I don't know what went wrong with my code.
Here's my work so far
img = imread("moon.png", 0);
Mat convoSharp() {
//creating new image
Mat res = img.clone();
for (int y = 0; y < res.rows; y++) {
for (int x = 0; x < res.cols; x++) {
res.at<uchar>(y, x) = 0.0;
}
}
//variable declaration
//change -5 to -4 for original result.
int filter[3][3] = { {0,1,0},{1,-4,1},{0,1,0} };
//int filter[3][3] = { {-1,-2,-1},{0,0,0},{1,2,1} };
int height = img.rows;
int width = img.cols;
int **temp = new int*[height];
for (int i = 0; i < height; i++) {
temp[i] = new int[width];
}
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
temp[i][j] = 0;
}
}
int filterHeight = 3;
int filterWidth = 3;
int newImageHeight = height - filterHeight + 1;
int newImageWidth = width - filterWidth + 1;
int i, j, h, w;
//convolution
for (i = 0; i < newImageHeight; i++) {
for (j = 0; j < newImageWidth; j++) {
for (h = i; h < i + filterHeight; h++) {
for (w = j; w < j + filterWidth; w++) {
temp[i][j] += filter[h - i][w - j] * (int)img.at<uchar>(h, w);
}
}
}
}
//find max and min
int max = 0;
int min = 100;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if (temp[i][j] > max) {
max = temp[i][j];
}
if (temp[i][j] < min) {
min = temp[i][j];
}
}
}
//clamp 0 - 255
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
res.at<uchar>(i, j) = 0 + (temp[i][j] - min)*(255 - 0) / (max - min);
}
}
//empty the temp array
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
temp[i][j] = 0;
}
}
//img - res and store it in temp array
for (int y = 0; y < res.rows; y++) {
for (int x = 0; x < res.cols; x++) {
//int a = (int)img.at<uchar>(y, x) - (int)res.at<uchar>(y, x);
//cout << a << endl;
temp[y][x] = (int)img.at<uchar>(y, x) - (int)res.at<uchar>(y, x);
}
}
//find the new max and min
max = 0;
min = 100;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if (temp[i][j] > max) {
max = temp[i][j];
}
if (temp[i][j] < min) {
min = temp[i][j];
}
}
}
//clamp it back to 0-255
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
res.at<uchar>(i, j) = 0 + (temp[i][j] - min)*(255 - 0) / (max - min);
temp[i][j] = (int)res.at<uchar>(i, j);
}
}
return res;
}
And here's the result
as you can see in my code above , i already normalize the pixel value to 0-255. i still don't know what went wrong here. Can anyone here explain why is that ?
The greyness is because, as Max suggested in his answer, you are scaling to the 0-255 range, not clamping (as your comments in the code suggest).
However, that is not all of the issues in your code. The output of the Laplace operator contains negative values. You nicely store these in an int. But then you scale and copy over to a char. Don't do that!
You need to add the result of the Laplace unchanged to your image. This way, some pixels in your image will become darker, and some lighter. This is what causes the edges to appear sharper.
Simply skip some of the loops in your code, and keep one that does temp = img - temp. That result you can freely scale or clamp to the output range and cast to char.
To clamp, simply set any pixel values below 0 to 0, and any above 255 to 255. Don't compute min/max and scale as you do, because there you reduce contrast and create the greyish wash over your image.
Your recent question is quite similar (though the problem in the code was different), read my answer there again, it suggests a way to further simplify your code so that img-Laplace becomes a single convolution.
The problem is that you are clamping and rescaling the image. Look at the bottom left border of the moon: There are very bright pixels next to very dark pixels, and then some gray pixels right besides the bright ones. Your sharpening filter will really spike on that bright border and increase the maximum. Similarly, the black pixels will be reduced even further.
You then determine minimum and maximum and rescale the entire image. This necessarily means the entire image will lose contrast when displayed in the previous gray scale, because your filter outputted pixel values above 255 and below 0.
Looks closely at the border of the moon in the output image:
There is a black halo (the new 0) and a bright, sharp edge (the new 255). (The browser image scaling made it less crisp in this screenshot, look at your original output). Everything else was squashed by the rescaling, so what was previous black (0) is now dark gray.
I was learning filters in OpenCV, but I'm a little confused about the Laplacian filter. My result is very different from the Laplacian filter in OpenCV lib.
For first, I use a Gaussian filter for the image:
Mat filtroGauss(Mat src){
Mat gauss = src.clone();
Mat temp(src.rows+2,src.cols+2,DataType<uchar>::type);
int y,x;
for (y=0; y<src.rows; y++){
for (x=0; x<src.cols; x++) temp.at<uchar>(y+1,x+1) = src.at<uchar>(y,x);
}
int mask[lenMask*lenMask];
mask[0] = mask[2] = mask[6] = mask[8] = 1;
mask[1] = mask[3] = mask[5] = mask[7] = 2;
mask[4] = 4;
int denominatore = 0;
for (int i=0; i<lenMask*lenMask; i++) denominatore += mask[i];
int value[lenMask*lenMask];
for(y=0; y<src.rows; y++){
for (x=0; x<src.cols; x++){
value[0] = temp.at<uchar>(y-1,x-1)*mask[0];
value[1] = temp.at<uchar>(y-1,x)*mask[1];
value[2] = temp.at<uchar>(y-1,x+1)*mask[2];
value[3] = temp.at<uchar>(y,x-1)*mask[3];
value[4] = temp.at<uchar>(y,x)*mask[4];
value[5] = temp.at<uchar>(y,x+1)*mask[5];
value[6] = temp.at<uchar>(y+1,x-1)*mask[6];
value[7] = temp.at<uchar>(y+1,x)*mask[7];
value[8] = temp.at<uchar>(y+1,x+1)*mask[8];
int avg = 0;
for(int i=0; i<lenMask*lenMask; i++)avg+=value[i];
avg = avg/denominatore;
gauss.at<uchar>(y,x) = avg;
}
}
return gauss;
}
Then I use the Laplacian function:
L(y,x) = f(y-1,x) + f(y+1,x) + f(y,x-1) + f(y,x+1) + 4*f(y,x)
Mat filtroLaplace(Mat src){
Mat output = src.clone();
Mat temp = src.clone();
int y,x;
for (y =1; y<src.rows-1; y++){
for(x =1; x<src.cols-1; x++){
output.at<uchar>(y,x) = temp.at<uchar>(y-1,x) + temp.at<uchar>(y+1,x) + temp.at<uchar>(y,x-1) + temp.at<uchar>(y,x+1) -4*( temp.at<uchar>(y,x));
}
}
return output;
}
And here is the final result from my code:
OpenCV result:
Let's rewrite the function a little, so it's easier to discuss:
cv::Mat filtroLaplace(cv::Mat src)
{
cv::Mat output = src.clone();
for (int y = 1; y < src.rows - 1; y++) {
for (int x = 1; x < src.cols - 1; x++) {
int sum = src.at<uchar>(y - 1, x)
+ src.at<uchar>(y + 1, x)
+ src.at<uchar>(y, x - 1)
+ src.at<uchar>(y, x + 1)
- 4 * src.at<uchar>(y, x);
output.at<uchar>(y, x) = sum;
}
}
return output;
}
The source of your problem is sum. Let's examine its range in scope of this algorithm, by taking the two extremes:
Black pixel, surrounded by 4 white. That means 255 + 255 + 255 + 255 - 4 * 0 = 1020.
White pixel, surrounded by 4 black. That means 0 + 0 + 0 + 0 - 4 * 255 = -1020.
When you perform output.at<uchar>(y, x) = sum; there's an implicit cast of the int back to unsigned char -- the high order bits simply get chopped off and the value overflows.
The correct approach to handle this situation (which OpenCV takes), is to perform saturation before the actual cast. Essentially
if (sum < 0) {
sum = 0;
} else if (sum > 255) {
sum = 255;
}
OpenCV provides function cv::saturate_cast<T> to do just this.
There's an additional problem that you're not handling the edge rows/columns of the input image -- you just leave them at the original value. Since you're not asking about that, I'll leave solving that as an excercise to the reader.
Code:
cv::Mat filtroLaplace(cv::Mat src)
{
cv::Mat output = src.clone();
for (int y = 1; y < src.rows - 1; y++) {
for (int x = 1; x < src.cols - 1; x++) {
int sum = src.at<uchar>(y - 1, x)
+ src.at<uchar>(y + 1, x)
+ src.at<uchar>(y, x - 1)
+ src.at<uchar>(y, x + 1)
- 4 * src.at<uchar>(y, x);
output.at<uchar>(y, x) = cv::saturate_cast<uchar>(sum);
}
}
return output;
}
Sample input:
Output of corrected filtroLaplace:
Output of cv::Laplacian:
I am trying to implement the Matlab function imquantize using opencv. Which opencv thresholding function I should use to implement Matlab function multithresh? Once thresholding has been done how do I label the pixels according to the threshold? Is this the right way to implement imquantize ? Are there any other function's I should include in the code?
There is an implementation based on OpenCV here, where you should probably get the idea:
cv::Mat
imquantize(const cv::Mat& in, const arma::fvec& thresholds) {
BOOST_ASSERT_MSG(cv::DataType<float>::type == in.type(), "input is not of type float");
cv::Mat index(in.size(), in.type(), cv::Scalar::all(1));
for (int i = 0; i < thresholds.size() ; i++) {
cv::Mat temp = (in > thresholds(i)) / 255;
temp.convertTo(temp, cv::DataType<float>::type);
index += temp;
}
return index;
}
Updated: thresholds are the vector of the float threshold values (uniform distributed to # of levels that you want to quantize within [0, 1]). Check the code snippet of how it is used:
const float step = 1./levels[i];
arma::fvec thresh = arma::linspace<arma::fvec>(step, 1.-step, levels[i]-1);
channels[i] = imquantize(channels[i], thresh);
I suppose you are looking for something like this
/*function imquantize
* 'inputImage' is the input image.
* 'levels' is an array of threholds
* 'quantizedImage' is the reurned image
* with quantized levels.
*/
Mat imquantize(Mat inputImage, vector<vector<int> > levels)
{
//initialise output label matrix
Mat quantizedImage(inputImage.size(), inputImage.type(), Scalar::all(1));
//Apply labels to the pixels according to the thresholds
for (int i = 0; i < inputImage.cols; i++)
{
for (int j = 0; j < inputImage.rows; j++)
{
// Check if image is grayscale or BGR
if(levels.size() == 1)
{
for (int k = 0; k < levels[0].size(); k++)
{
// if pixel < lowest threshold , then assign 0
if(inputImage.at<uchar>(j,i) <= levels[0][0])
{
quantizedImage.at<uchar>(j,i) = 0;
}
// if pixel > highest threshold , then assign 255
else if(inputImage.at<uchar>(j,i) >= levels[0][levels[0].size()-1])
{
quantizedImage.at<uchar>(j,i) = 255;
}
// Check the level borders for pixel and assign the corresponding
// upper bound quanta to the pixel
else
{
if(levels[0][k] < inputImage.at<uchar>(j,i) && inputImage.at<uchar>(j,i) <= levels[0][k+1])
{
quantizedImage.at<uchar>(j,i) = (k+1)*255/(levels[0].size());
}
}
}
}
else
{
Vec3b pair = inputImage.at<Vec3b>(j,i);
// Processing the Blue Channel
for (int k = 0; k < levels[0].size(); k++)
{
if( pair.val[0] <= levels[0][0])
{
quantizedImage.at<Vec3b>(j,i)[0] = 0;
}
else if( pair.val[0] >= levels[0][levels.size()-1])
{
quantizedImage.at<Vec3b>(j,i)[0] = 255;
}
else
{
if(levels[0][k] < pair.val[0] && pair.val[0] <= levels[0][k+1])
{
quantizedImage.at<Vec3b>(j,i)[0] = (k+1)*255/(levels[0].size());
}
}
}
// Processing the Green Channel
for (int k = 0; k < levels[1].size(); k++)
{
if( pair.val[1] <= levels[1][0])
{
quantizedImage.at<Vec3b>(j,i)[1] = 0;
}
else if( pair.val[1] >= levels[1][levels.size()-1])
{
quantizedImage.at<Vec3b>(j,i)[1] = 255;
}
else
{
if(levels[1][k] < pair.val[1] && pair.val[1] <= levels[1][k+1])
{
quantizedImage.at<Vec3b>(j,i)[1] = (k+1)*255/(levels[1].size());
}
}
}
// Processing the Red Channel
for (int k = 0; k < levels[2].size(); k++)
{
if( pair.val[2] <= levels[2][0])
{
quantizedImage.at<Vec3b>(j,i)[2] = 0;
}
else if( pair.val[2] >= levels[2][levels.size()-1])
{
quantizedImage.at<Vec3b>(j,i)[2] = 255;
}
else
{
if(levels[2][k] < pair.val[2] && pair.val[2] <= levels[2][k+1])
{
quantizedImage.at<Vec3b>(j,i)[2] = (k+1)*255/(levels[2].size());
}
}
}
}
}
}
return quantizedImage;
}
In this function the input had to be an Mat::Image and a 2D vector which can have different levels for different channels.