Common stones are colourless,looks dull,gray... it's expected they have low Saturation in HSV. Shadow is dark than... low brightness.
I've made my attempt around above points using MORPH_CLOSE to improve dark areas. This is the result, below is description and code.
Steps:
- Scale down the image because it's too big...
- Use Gaussian Blur to reduce variation a bit
- Convert to HSV
- Use MORPH_CLOSE on Saturation channel (use a structure half than wanted stone)
- Normalize the histogram to get more stable threshold
- Threshold dark saturation to select stones and shadows
The mask includes shadows. If this it's a problem you could try to remove shadows using the brightness channels... (shadows are dark)
- Use MORPH_CLOSE on brightness Channel (use same a structure as above)
- Normalize the histogram to get more stable threshold
- Threshold dark brightness to select just shadows
Get binary mask for the stone and process it with contour or blobs
- Make logical (stones+shadows) AND (NOT shadows) you should obtain just stones
- Find contours and process them
This is the code. Get BrightnessAndContrastAuto
here.
#define CL_BLU cv::Scalar(255, 0, 0 )
#define CL_GREEN cv::Scalar(0, 255, 0 )
#define CL_RED cv::Scalar(0, 0, 255 )
#define CL_WHITE cv::Scalar(255, 255, 255 )
const string winName="Stones";
Mat src,dst;
int minSizeMM,thStone,thShadow,removeShadow;
float pix2mm;
//helper function
void MorphClose(const Mat &imgIn,Mat &imgOut,int minThickess=2);
//get the source [here](http://answers.opencv.org/question/75510/how-to-make-auto-adjustmentsbrightness-and-contrast-for-image-android-opencv-image-correction/)
void BrightnessAndContrastAuto(const cv::Mat &src, cv::Mat &dst, float clipHistPercent=0);
// ON TRACK BAR FUNCTION
void onStonesTb(int, void*)
{
Mat blur,bwStones,bwShadow;
vector<vector<Point> > contours;
char buf[80];
cv::GaussianBlur(src,blur,Size(),2,2);
// convert to HSV
Mat src_hsv,brightness,saturation;
vector<Mat> hsv_planes;
cvtColor(blur, src_hsv, COLOR_BGR2HSV);
split(src_hsv, hsv_planes);
saturation = hsv_planes[1];
brightness = hsv_planes[2];
int minSizePx = cvRound(minSizeMM/pix2mm);
int closerSize = minSizePx /2.0;
//SELECT STONES (INCLUDING SHADOW)
MorphClose(saturation,saturation,closerSize);
BrightnessAndContrastAuto(saturation,saturation,1);
threshold(saturation,bwStones,thStone,255,THRESH_BINARY_INV); //Get 0..thStone
//show the selection
findContours(bwStones, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
for (int i = 0; i < contours.size(); i++)
cv::drawContours(saturation,contours,i,CL_WHITE,2);
imshow("Threshold Stones+Shadow on Saturation",saturation);
if(removeShadow)
{
//SELECT DARK AREAS (MAYBE SHADOWS)
MorphClose(brightness,brightness,closerSize);
BrightnessAndContrastAuto(brightness,brightness,1);
threshold(brightness,bwShadow,thShadow,255,THRESH_BINARY); //Get thShadow..255
//show the selection
contours.clear();
findContours(bwShadow, contours, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
for (int i = 0; i < contours.size(); i++)
cv::drawContours(brightness,contours,i,CL_WHITE,2);
imshow("Threshold Shadow on Brightness",brightness);
//remove shadows from stones
cv::bitwise_and(bwStones,bwShadow,bwStones);
}
//show the result
src.copyTo(dst);
findContours(bwStones, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
Point2f centroid;
for (int i = 0; i < contours.size(); i++)
{
//draw the contour
polylines(dst, contours[i], true, CL_GREEN,2);
//get the bounding rect
cv::Rect rect = cv::boundingRect(contours[i]);
//ignore small objects
if ( max(rect.width,rect.width) < minSizePx)
continue;
//calculate moments
cv::Moments M = cv::moments(contours[i], false);
//reject if area is 0
double ...
(more)
In my honest opinion, stereo will be much better then any segmentation/contour/shape based analysis, mainly because you cannot use the colour information as a decent source of info. So why not build a setup and experiment with that.
Another thing that you could investigate is the use of texture filters, to try to seperate the smooth surfaces of rocks compared to the soil, but that will be a challenging job also!