I am new to OpenCV and trying to find contours and draw rectangle on them, here's my code but its throwing cv::Exception when it comes to accumulatedweighted().
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <ctype.h>
using namespace cv;
using namespace std;
static void help()
{
cout << "\nThis is a Example to implement CAMSHIFT to detect multiple motion objects.\n";
}
Rect rect;
VideoCapture capture;
Mat currentFrame, currentFrame_grey, differenceImg, oldFrame_grey,background;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
bool first = true;
int main(int argc, char* argv[])
{
//Create a new movie capture object.
capture.open(0);
if(!capture.isOpened())
{
//error in opening the video input
cerr << "Unable to open video file: " /*<< videoFilename*/ << endl;
exit(EXIT_FAILURE);
}
//capture current frame from webcam
capture >> currentFrame;
//Size of the image.
CvSize imgSize;
imgSize.width = currentFrame.size().width; //img.size().width
imgSize.height = currentFrame.size().height; ////img.size().height
//Images to use in the program.
currentFrame_grey.create( imgSize, IPL_DEPTH_8U);//image.create().
while(1)
{
capture >> currentFrame;//VideoCapture& VideoCapture::operator>>(Mat& image)
//Convert the image to grayscale.
cvtColor(currentFrame,currentFrame_grey,CV_RGB2GRAY);//cvtColor()
// Converting Original image to make both background n original image same
currentFrame.convertTo(currentFrame,CV_32FC3);
background = Mat::zeros(currentFrame.size(), CV_32FC3);
//Here its throwing exception
accumulateWeighted(currentFrame,background,1.0,NULL);
imshow("Background",background);
if(first) //Capturing Background for the first time
{
differenceImg = currentFrame_grey.clone();//img1 = img.clone()
oldFrame_grey = currentFrame_grey.clone();//img2 = img.clone()
convertScaleAbs(currentFrame_grey, oldFrame_grey, 1.0, 0.0);//convertscaleabs()
first = false;
continue;
}
//Minus the current frame from the moving average.
absdiff(oldFrame_grey,currentFrame_grey,differenceImg);//absDiff()
//bluring the differnece image
blur(differenceImg, differenceImg, imgSize);//blur()
//apply threshold to discard small unwanted movements
threshold(differenceImg, differenceImg, 25, 255, CV_THRESH_BINARY);//threshold()
//find contours
findContours(differenceImg,contours,hierarchy,CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); //findcontours()
//draw bounding box around each contour
//for(; contours! = 0; contours = contours->h_next)
for(int i = 0; i < contours.size(); i++)
{
rect = boundingRect(contours[i]); //extract bounding box for current contour
//drawing rectangle
rectangle(currentFrame, cvPoint(rect.x, rect.y), cvPoint(rect.x+rect.width, rect.y+rect.height), cvScalar(0, 0, 255, 0), 2, 8, 0);
}
//New Background
convertScaleAbs(currentFrame_grey, oldFrame_grey, 1.0, 0.0);
//display colour image with bounding box
imshow("Output Image", currentFrame);//imshow()
//display threshold image
imshow("Difference image", differenceImg);//imshow()
//clear memory and contours
//cvClearMemStorage( storage );
//contours = 0;
contours.clear();
//background = currentFrame;
//press Esc to exit
char c = cvWaitKey(33);
if( c == 27 ) break;
}
// Destroy All Windows.
destroyAllWindows();
return 0;
}
Please Help to solve this.
First of all, I don't really get the idea of calling accumulateWeighted with alpha = 1.0. If you look at the definition of accumulateWeighted in the doc, you will see that with alpha = 1.0 it is basically equivalent to copy currentFrame into background at each iteration.
Moreover, it is an accumulation function, to accumulate image changes over time into a new image. What is the interest of it if you reset background at every loop with background = Mat::zeros(currentFrame.size(), CV_32FC3); ?
This being said, there is a little flaw in your code with the 4th argument of the function. You wrote accumulateWeighted(currentFrame,background,1.0,NULL);. If you look into the documentation you will find that the 4th argument is a Mask, and is optional. Passing a NULL pointer here might be the source of your exception. Why don't you call the function like this : accumulateWeighted(currentFrame,background,1.0); ?
Hope this helps,
Ben
Related
Hi. I have the above image and use the "findContours" function.
And then I use the "convexity defects" functions to find the corner points.
The result is as follows.
The problem with this code is that it can not find the rounded corners.You can not find a point like the following.
This is my code
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/videoio.hpp"
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>
#include <iostream>
#include <sstream>
#include <fstream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
cv::Mat image = cv::imread("find_Contours.png");
//Prepare the image for findContours
cv::cvtColor(image, image, CV_BGR2GRAY);
cv::threshold(image, image, 128, 255, CV_THRESH_BINARY);
//Find the contours. Use the contourOutput Mat so the original image doesn't get overwritten
std::vector<std::vector<cv::Point> > contours;
cv::Mat contourOutput = image.clone();
cv::findContours(contourOutput, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
////convexityDefects
vector<vector<Point> >hull(contours.size());
vector<vector<int> > hullsI(contours.size()); // Indices to contour points
vector<vector<Vec4i>> defects(contours.size());
for (int i = 0; i < contours.size(); i++)
{
convexHull(contours[i], hull[i], false);
convexHull(contours[i], hullsI[i], false);
if (hullsI[i].size() > 3) // You need more than 3 indices
{
convexityDefects(contours[i], hullsI[i], defects[i]);
}
}
///// Draw convexityDefects
for (int i = 0; i < contours.size(); ++i)
{
for (const Vec4i& v : defects[i])
{
float depth = v[3]/256;
if (depth >= 0) // filter defects by depth, e.g more than 10
{
int startidx = v[0]; Point ptStart(contours[i][startidx]);
int endidx = v[1]; Point ptEnd(contours[i][endidx]);
int faridx = v[2]; Point ptFar(contours[i][faridx]);
circle(image, ptFar, 4, Scalar(255, 255, 255), 2);
cout << ptFar << endl;
}
}
}
//
cv::imshow("Input Image", image);
cvMoveWindow("Input Image", 0, 0);
//
waitKey(0);
}
Can someone make the code and find the red dot? please help.
now i want find "convexity defects" from inside,not outside like this image:
Someone can help me??
It is very important to use
convexHull(contours[i], hullsI[i], true);
That is, with the last argument "true" for indices. I'm almost certain this is the reason it cannot find all the defects. Before fixing this, it is not much sense try to find other bugs (if any).
Suppose I have an image. I basically want to make boundary across a particular colour that I want. I know the hsv minimum and maximum scalar values of that colour. But I don't know how to proceed further.
#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include<stdio.h>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
VideoCapture cap(0);
while(true)
{
Mat img;
cap.read(img);
Mat dst;
Mat imghsv;
cvtColor(img, imghsv, COLOR_BGR2HSV);
inRange(imghsv,
Scalar(0, 30, 0),
Scalar(20, 150, 255),
dst
);
imshow("name",dst);
if (waitKey(30) == 27) //wait for 'esc' key press for 30ms
{
cout << "esc key is pressed by user" << endl;
break;
}
}
}
The inrange function works well but I am not able to draw a boundary across whatever is white (I mean whichever pixel is in the range specified)
You need to first segment the color, and then find the contours of the segmented image.
SEGMENT THE COLOR
Working in HSV is in general a good idea to segment colors. Once you have the correct lower and upper boundary, you can easily segment the color.
A simple approach is to use inRange.
You can find how to use it here for example.
FIND BOUNDARIES
Once you have the binary mask (obtained through segmentation), you can find its boundaries using findContours. You can refer to this or this to know how to use findContours to detect the boundary, and drawContours to draw it.
UPDATE
Here a working example on how to draw a contour on segmented objects.
I used some morphology to clean the mask, and changed to tracked color to be blue, but you can put your favorite color.
#include<opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
VideoCapture cap(0);
while (true)
{
Mat img;
cap.read(img);
Mat dst;
Mat imghsv;
cvtColor(img, imghsv, COLOR_BGR2HSV);
inRange(imghsv, Scalar(110, 100, 100), Scalar(130, 255, 255), dst); // Detect blue objects
// Remove some noise using morphological operators
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(7,7));
morphologyEx(dst, dst, MORPH_OPEN, kernel);
// Find contours
vector<vector<Point>> contours;
findContours(dst.clone(), contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
// Draw all contours (green)
// This
drawContours(img, contours, -1, Scalar(0,255,0));
// If you want to draw a contour for a particular one, say the biggest...
// Find the biggest object
if (!contours.empty())
{
int idx_biggest = 0;
int val_biggest = contours[0].size();
for (int i = 0; i < contours.size(); ++i)
{
if (val_biggest < contours[i].size())
{
val_biggest = contours[i].size();
idx_biggest = i;
}
}
// Draw a single contour (blue)
drawContours(img, contours, idx_biggest, Scalar(255,0,0));
// You want also the rotated rectangle (blue) ?
RotatedRect r = minAreaRect(contours[idx_biggest]);
Point2f pts[4];
r.points(pts);
for (int j = 0; j < 4; ++j)
{
line(img, pts[j], pts[(j + 1) % 4], Scalar(0, 0, 255), 2);
}
}
imshow("name", dst);
imshow("image", img);
if (waitKey(30) == 27) //wait for 'esc' key press for 30ms
{
cout << "esc key is pressed by user" << endl;
break;
}
}
}
If you want a particular hue to be detected then you can create a mask to select only the particular color from your original image.
on the hue channel (img):
cv::Mat mask = cv::Mat::zeros(img.size(),CV_8UC1);
for(int i=0;i<img.rows;i++){
for(int j=0;j<img.cols;i++){
if(img.at<uchar>(i,j)==(uchar)specific_hue){
mask.at<uchar>(i,j)=(uchar)255;
}
}
}
color_img.copyTo(masked_image, mask);
If you want something less rigorous, you can define a range around the color to allow more image to pass through the mask.
cv::Mat mask = cv::Mat::zeros(img.size(),CV_8UC1);
int threshold = 5;
for(int i=0;i<img.rows;i++){
for(int j=0;j<img.cols;i++){
if((img.at<uchar>(i,j)>(uchar)(specific_hue - threshold)) && (img.at<uchar>(i,j)<(uchar)(specific_hue + threshold))){
mask.at<uchar>(i,j)=(uchar)255;
}
}
}
color_img.copyTo(masked_image, mask);
I need to count the number of metal balls inside a small metal cup.
I tried template matching but it showed only one result having most probability.
But i need the count of total metal balls visible.
Since background too is metallic i was unable to do color thresholding.
I tried a method of finding the first occurrence using template matching and then fill that area with RGB(0,0,0) and again did the template matching on that image, but several false detections are occurring.
My primary requirement is to find the images that have three balls filled inside the cup and any other quantities other than three should not be detected.
Please see the images of different quantities filled inside the cup
Use Hough circles - see the OpenCV documentation for how to do this. Then just count the circles that are with some empirically determined radius range.
Here are some results and code that will enable you to do what you want:
#include <iostream> // std::cout
#include <algorithm> // std::sort
#include <vector> // std::vector
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
using namespace std;
using namespace cv;
bool circle_compare (Vec3f i,Vec3f j) { return (i[2]>j[2]); }
int main(int argc, char** argv)
{
/// Read the image
Mat one = imread("one.jpg", 1 );
Mat two = imread("two.jpg", 1 );
Mat three = imread("three.jpg", 1 );
Mat four = imread("four.jpg", 1 );
if(!one.data || !two.data || !three.data || !four.data)
{
return -1;
}
// put all the images into one
Mat src(one.rows * 2, one.cols * 2, one.type());
Rect roi1(0, 0, one.cols, one.rows);
one.copyTo(src(roi1));
Rect roi2(one.cols, 0, one.cols, one.rows);
two.copyTo(src(roi2));
Rect roi3(0, one.rows, one.cols, one.rows);
three.copyTo(src(roi3));
Rect roi4(one.cols, one.rows, one.cols, one.rows);
four.copyTo(src(roi4));
// extract the blue channel because the circles show up better there
vector<cv::Mat> channels;
cv::split(src, channels);
cv::Mat blue;
GaussianBlur( channels[0], blue, Size(7, 7), 4, 4 );
vector<Vec3f> circles;
vector<Vec3f> candidate_circles;
/// Find the circles
HoughCircles( blue, candidate_circles, CV_HOUGH_GRADIENT, 1, 1, 30, 55);//, 0, 200 );
// sort candidate cirles by size, largest first
// so the accepted circles are the largest that meet other criteria
std::sort (candidate_circles.begin(), candidate_circles.end(), circle_compare);
/// Draw the circles detected
for( size_t i = 0; i < candidate_circles.size(); ++i )
{
Point center(cvRound(candidate_circles[i][0]), cvRound(candidate_circles[i][4]));
int radius = cvRound(candidate_circles[i][5]);
// skip over big circles
if(radius > 35)
continue;
// test whether centre of candidate_circle is inside of accepted circle
bool inside = false;
for( size_t j = 0; j < circles.size(); ++j )
{
Point c(cvRound(circles[j][0]), cvRound(circles[j][6]));
int r = cvRound(circles[j][7]);
int d = sqrt((center.x - c.x) * (center.x - c.x) + (center.y - c.y) * (center.y - c.y));
if(d <= r)
{
inside = true; // candidate is inside an existing circle
}
}
if(inside)
continue;
// accept the current candidate circle then draw it
circles.push_back(candidate_circles[i]);
circle( src, center, 3, Scalar(0,255,0), -1, 8, 0 );
circle( src, center, radius, Scalar(0,0,255), 3, 8, 0 );
}
// now fill the circles in the quadrant that has three balls
vector<Vec3f> tl, tr, bl, br;
for( size_t i = 0; i < circles.size(); ++i )
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][8]));
int radius = cvRound(circles[i][9]);
if(center.x < one.cols)
{
if(center.y < one.rows)
{
tl.push_back(circles[i]);
}
else
{
bl.push_back(circles[i]);
}
}
else
{
if(center.y < one.rows)
{
tr.push_back(circles[i]);
}
else
{
br.push_back(circles[i]);
}
}
vector<vector<Vec3f>> all;
all.push_back(tl);
all.push_back(tr);
all.push_back(bl);
all.push_back(bl);
for( size_t k = 0; k < all.size(); ++k )
{
if(all[k].size() == 3)
{
for( size_t i = 0; i < all[k].size(); ++i )
{
Point center(cvRound(all[k][i][0]), cvRound(all[k][i][10]));
int radius = cvRound(all[k][i][11]);
circle( src, center, radius, Scalar(0,255, 255), -1, 4, 0 );
}
}
}
}
// resize for easier display
resize(src, src, one.size());
/// Save results and display them
imwrite("balls.png", src);
//namedWindow( "Balls", CV_WINDOW_AUTOSIZE );
imshow( "Balls", src );
waitKey(0);
return 0;
}
Maybe you can try the template matching algorithm, but with a twist. Don't look for circles (balls). But look for the small triangle in center of the 3 balls.
You have to take into account the rotation of the triangle, but simple contour processing should do the job.
define ROI in center of the image (center of cup)
run some edge detector and contour detection
simplify every suitable contour found
check if found contour has 3 corners with angle sharp enough to form an triangle
To distinguish case with more than 3 balls check also overall intensity of the image. Photo of 3 balls only should have quite low intensity compared to one with more balls.
EDIT:
2013-11-08 6.15PM GMT
In this case of image, might be actually helpfull to use watershed segmentation algorithm.
This algorithm is part of OpenCV, I don't now which version is the first one, but it seems it's in OCV 3.0.0: http://docs.opencv.org/trunk/modules/imgproc/doc/miscellaneous_transformations.html?highlight=watershed#cv2.watershed
Some basic for watershed on wiki: http://en.wikipedia.org/wiki/Watershed_%28image_processing%29
The objective is to detect the 5 white circles in the image.The test image in which the circles have to be detected is the one shown here 640x480
Please download the original image here,1280x1024
I am using different methods to bring out a evaluation of various circle/ellipse detection methods. But somehow I am not able to fix my simple Hough transform code. It does not detect any circles. I am not clear whether the problem is with pre-processing step, or the parameters of the HoughCircle. I have gone through all the similar questions in the forum, but still not able to fix the issue. This is my code. Please help me in this regards..
Header file
#ifndef IMGPROCESSOR_H
#define IMGPROCESSOR_H
// OpenCV Library
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
class ImgProcessor{
public:
Mat OpImg ;
ImgProcessor();
~ImgProcessor();
//aquire filter methods to image
int Do_Hough(Mat IpImg);
};
#endif /* ImgProcessor_H */
Source file
#include "ImgProcessor.h"
#include <opencv2\opencv.hpp>
#include "opencv2\imgproc\imgproc.hpp"
#include "opencv2\imgproc\imgproc_c.h"
#include <vector>
using namespace cv;
ImgProcessor::ImgProcessor(){
return;
}
ImgProcessor::~ImgProcessor(){
return;
}
//Apply filtering for the input image
int ImgProcessor::Do_Hough(Mat IpImg)
{
//Parameter Initialization________________________________________________________
double sigma_x, sigma_y, thresh=250, max_thresh = 255;
int ksize_w = 5 ;
int ksize_h = 5;
sigma_x = 0.3*((ksize_w-1)*0.5 - 1) + 0.8 ;
sigma_y = 0.3*((ksize_h-1)*0.5 - 1) + 0.8 ;
vector<Vec3f> circles;
//Read the image as a matrix
Mat TempImg;
//resize(IpImg, IpImg ,Size(), 0.5,0.5, INTER_AREA);
//Preprocessing__________________________________________________________
//Perform initial smoothing
GaussianBlur( IpImg, TempImg, Size(ksize_w, ksize_h),2,2);
//perform thresholding
threshold(TempImg,TempImg, thresh,thresh, 0);
//Remove noise by gaussian smoothing
GaussianBlur( TempImg, TempImg, Size(ksize_w, ksize_h),2,2);
/*imshow("Noisefree Image", TempImg);
waitKey(10000);*/
//Obtain edges
Canny(TempImg, TempImg, 255,240 , 3);
imshow("See Edges", TempImg);
waitKey(10000);
//Increase the line thickness
//dilate(TempImg,TempImg,0,Point(-1,-1),3);
//Hough Circle Method______________________________________________________________
// Apply the Hough Transform to find the circles
HoughCircles( TempImg, circles, 3, 1, TempImg.rows/32, 255, 240, 5, 0 );
// Draw the circles detected
for( size_t i = 0; i < circles.size(); i++ )
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
// circle center
circle( IpImg, center, 3, Scalar(0,255,0), -1, 8, 0 );
// circle outline
circle( IpImg, center, radius, Scalar(0,0,255), 3, 8, 0 );
}
// Show your results
namedWindow( "Hough Circle Transform", WINDOW_AUTOSIZE );
imshow( "Hough Circle Transform", IpImg );
// waitKey(0);
return 0;
}
int main(int argc, char** argv)
{
ImgProcessor Iclass;
//char* imageName = argv[1];
string imageName = "D:/Projects/test_2707/test_2707/1.bmp";
Mat IpImg = imread( imageName );
cvtColor(IpImg, IpImg,6,CV_8UC1);
Iclass.Do_Hough(IpImg);
/*Iclass.Do_Contours(IpImg);*/
return 0;
}
The code seems fine, other than for:
HoughCircles( TempImg, circles, 3, 1, TempImg.rows/32, 255, 240, 5, 0 );
Does number 3 in the parameter list correspond to CV_HOUGH_GRADIENT ? It is always better to use definitions instead of numbers.
May be you should test it first with an image with bigger circles. Once you are sure that the rest of the code is correct, you can tune the parameters of HoughCircles.
I was trying to sharpening on some standard image from Gonzalez books. Below are some code that I have tried but it doesn't get closer to the results of the sharpened image.
cvSmooth(grayImg, grayImg, CV_GAUSSIAN, 3, 0, 0, 0);
IplImage* laplaceImg = cvCreateImage(cvGetSize(oriImg), IPL_DEPTH_16S, 1);
IplImage* abs_laplaceImg = cvCreateImage(cvGetSize(oriImg), IPL_DEPTH_8U, 1);
cvLaplace(grayImg, laplaceImg, 3);
cvConvertScaleAbs(laplaceImg, abs_laplaceImg, 1, 0);
IplImage* dstImg = cvCreateImage(cvGetSize(oriImg), IPL_DEPTH_8U, 1);
cvAdd(abs_laplaceImg, grayImg, dstImg, NULL);
Before Sharpening
My Sharpening Result
Desired Result
Absolute Laplace
I think the problem is that you are blurring the image before take the 2nd derivate.
Here is the working code with the C++ API (I'm using Opencv 2.4.3). I tried also with MATLAB and the result is the same.
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int /*argc*/, char** /*argv*/) {
Mat img, imgLaplacian, imgResult;
//------------------------------------------------------------------------------------------- test, first of all
// now do it by hand
img = (Mat_<uchar>(4,4) << 0,1,2,3,4,5,6,7,8,9,0,11,12,13,14,15);
// first, the good result
Laplacian(img, imgLaplacian, CV_8UC1);
cout << "let opencv do it" << endl;
cout << imgLaplacian << endl;
Mat kernel = (Mat_<float>(3,3) <<
0, 1, 0,
1, -4, 1,
0, 1, 0);
int window_size = 3;
// now, reaaallly by hand
// note that, for avoiding padding, the result image will be smaller than the original one.
Mat frame, frame32;
Rect roi;
imgLaplacian = Mat::zeros(img.size(), CV_32F);
for(int y=0; y<img.rows-window_size/2-1; y++) {
for(int x=0; x<img.cols-window_size/2-1; x++) {
roi = Rect(x,y, window_size, window_size);
frame = img(roi);
frame.convertTo(frame, CV_32F);
frame = frame.mul(kernel);
float v = sum(frame)[0];
imgLaplacian.at<float>(y,x) = v;
}
}
imgLaplacian.convertTo(imgLaplacian, CV_8U);
cout << "dudee" << imgLaplacian << endl;
// a little bit less "by hand"..
// using cv::filter2D
filter2D(img, imgLaplacian, -1, kernel);
cout << imgLaplacian << endl;
//------------------------------------------------------------------------------------------- real stuffs now
img = imread("moon.jpg", 0); // load grayscale image
// ok, now try different kernel
kernel = (Mat_<float>(3,3) <<
1, 1, 1,
1, -8, 1,
1, 1, 1); // another approximation of second derivate, more stronger
// do the laplacian filtering as it is
// well, we need to convert everything in something more deeper then CV_8U
// because the kernel has some negative values,
// and we can expect in general to have a Laplacian image with negative values
// BUT a 8bits unsigned int (the one we are working with) can contain values from 0 to 255
// so the possible negative number will be truncated
filter2D(img, imgLaplacian, CV_32F, kernel);
img.convertTo(img, CV_32F);
imgResult = img - imgLaplacian;
// convert back to 8bits gray scale
imgResult.convertTo(imgResult, CV_8U);
imgLaplacian.convertTo(imgLaplacian, CV_8U);
namedWindow("laplacian", CV_WINDOW_AUTOSIZE);
imshow( "laplacian", imgLaplacian );
namedWindow("result", CV_WINDOW_AUTOSIZE);
imshow( "result", imgResult );
while( true ) {
char c = (char)waitKey(10);
if( c == 27 ) { break; }
}
return 0;
}
Have fun!
I think the main problem lies in the fact that you do img + laplace, while img - laplace would give better results. I remember that img - 2*laplace was best, but I cannot find where I read that, probably in one of the books I read in university.
You need to do img - laplace instead of img + laplace.
laplace: f(x,y) = f(x-1,y+1) + f(x-1,y-1) + f(x,y+1) + f(x+1,y) - 4*f(x,y)
So, if you see subtract laplace from the original image you would see that the minus sign in front of 4*f(x,y) gets negated and this term becomes positive.
You could also have kernel with -5 in the center pixel instead of -4 to make the laplacian a one-step process instead of getting the getting the laplace and doing img - laplace Why? Try deriving that yourself.
This would be the final kernel.
Mat kernel = (Mat_(3,3) <<
-1, 0, -1,
0, -5, 0,
-1, 0, -1);
It is indeed a well-known result in image processing that if you subtract its Laplacian from an image, the image edges are amplified giving a sharper image.
Laplacian Filter Kernel algorithm: sharpened_pixel = 5 * current – left – right – up – down
enter image description here
So the Code will look like these:
void sharpen(const Mat& img, Mat& result)
{
result.create(img.size(), img.type());
//Processing the inner edge of the pixel point, the image of the outer edge of the pixel should be additional processing
for (int row = 1; row < img.rows-1; row++)
{
//Front row pixel
const uchar* previous = img.ptr<const uchar>(row-1);
//Current line to be processed
const uchar* current = img.ptr<const uchar>(row);
//new row
const uchar* next = img.ptr<const uchar>(row+1);
uchar *output = result.ptr<uchar>(row);
int ch = img.channels();
int starts = ch;
int ends = (img.cols - 1) * ch;
for (int col = starts; col < ends; col++)
{
//The traversing pointer of the output image is synchronized with the current row, and each channel value of each pixel in each row is given a increment, because the channel number of the image is to be taken into account.
*output++ = saturate_cast<uchar>(5 * current[col] - current[col-ch] - current[col+ch] - previous[col] - next[col]);
}
} //end loop
//Processing boundary, the peripheral pixel is set to 0
result.row(0).setTo(Scalar::all(0));
result.row(result.rows-1).setTo(Scalar::all(0));
result.col(0).setTo(Scalar::all(0));
result.col(result.cols-1).setTo(Scalar::all(0));
}
int main()
{
Mat lena = imread("lena.jpg");
Mat sharpenedLena;
ggicci::sharpen(lena, sharpenedLena);
imshow("lena", lena);
imshow("sharpened lena", sharpenedLena);
cvWaitKey();
return 0;
}
If you are a lazier. Have fun with the following.
int main()
{
Mat lena = imread("lena.jpg");
Mat sharpenedLena;
Mat kernel = (Mat_<float>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);
cv::filter2D(lena, sharpenedLena, lena.depth(), kernel);
imshow("lena", lena);
imshow("sharpened lena", sharpenedLena);
cvWaitKey();
return 0;
}
And the result like these.enter image description here