I am using OpenCV to manipulate some images.
Suppose that image.png is black/white image (only B or W for pixel colors).
For example, if we print the colors for 3rd line, it could be:
WWWWWBBBWWWWWWBBBBBBWWWWWBBWWWW
I'd like to save info on each sequence of black pixels, I mean, I'd like to be able to compute, for each row, the values:
number of black sequences in row i: (3 on example above)
x-coordinate of end pixels for each black sequence in row i: (6,8 and 15,20 and 26,27 on example above)
length of each black sequence on row i: (l1=3,l2=6,l3=2 on example above) (this is easy assuming item above is done)
I'm using some for loop and testing if color is black. When it is black, I save the x coordinate and start other loop inside to run from this coordinate to the end of line, testing if the color is white. When it finds white color, it stops and save the previous coordinate.
This works to compute only the length of first sequence of black pixels. I don't know how to go to next (I even don't know how many there are).
Here is the main part of code (with some trash code):
for(int y=0;y<img.rows;y++) //img.rows
{
for(int x=0;x<img.cols;x++)
{
Vec3b color = image.at<Vec3b>(Point(x,y));
printf("(%d,%d,%d)\n",color[1],color[2],color[3]);
if(color[0] == 0 && color[1] == 0 && color[2] == 0)
{
cor[0]='B';
ymax = y;
if (ymin == -1) { ymin = y; }
int xmin = x;
int diam_esq = img.cols/2-xmin;
double dist_esq = sqrt( (x-img.cols/2)*(x-img.cols/2) + (y-img.rows/2)*(y-img.rows/2) );
for(int z=x;z<img.cols;z++)
{
Vec3b colorz = image.at<Vec3b>(Point(z,y));
if(colorz[0] == 255 && colorz[1] == 255 && colorz[2] == 255)
{
int xmax = z-1;
int diam_dir = xmax-img.cols/2;
double dist_dir = sqrt( (z-1-img.cols/2)*(z-1-img.cols/2) + (y-img.rows/2)*(y-img.rows/2) );
int diam = xmax - xmin;
//printf("y=%*d, xmin=%*d, xmax=%*d, esq=%*d, dir=%*d, diam=%*d\n",5,y,5,xmin,5,xmax,5,diam_esq,5,diam_dir,5,diam);
printf("%*d%*d%*d%*d%*d%*d%*f%*f\n",5,y,5,xmin,5,xmax,5,diam_esq,5,diam_dir,5,diam,13,dist_esq,13,dist_dir);
break;
}
}
break;
}
}
//break; // only y=0
}
Here is some code that does what you want. It only prints out results. It doesn't save them anywhere but I assume you'll know how to do this.
To find black sequences in each row, there is no need to do nested for loops for each sequence, just keep track of whether or not you're inside a black sequence and if yes, of where it began. Also, I use Mat::ptr to allow for a more efficient traversal of your image, row by row.
for(int i = 0; i < image.rows; i++) {
Vec3b* ptr = image.ptr<Vec3b>(i);
bool blackSequence = false;
int blackSequenceStart = -1;
int numberOfBlackSequences = 0;
for(int j = 0; j < image.cols; j++) {
Vec3b color = ptr[j];
if(color[0] == 0 && !blackSequence) { // this is assuming that all pixels are either white or black
blackSequence = true;
blackSequenceStart = j;
numberOfBlackSequences++;
}
if(color[0] == 255 && blackSequence) {
blackSequence = false;
cout << "Row " << i << ": Sequence " << numberOfBlackSequences << " starts at " << blackSequenceStart
<< " and finishes at " << j - 1 << ". Total length: " << j - blackSequenceStart << endl;
}
if(j == image.cols - 1 && blackSequence) {
blackSequence = false;
cout << "Row " << i << ": Sequence " << numberOfBlackSequences << " starts at " << blackSequenceStart
<< " and finishes at " << j << ". Total length: " << j - blackSequenceStart + 1 << endl;
}
}
}
Related
When I train my classifier using opencv_traincascade by using local binary pattern (LBP), I get this written on console :
Number of unique features given windowSize [50,28] : 51408
How is this number calculated?
As usual with OpenCV, you can check the source code. It's basically computed from the window size.
That number comes out from featureEvaluator->getNumFeatures(). See here:
cout << "Number of unique features given windowSize ["
<< _cascadeParams.winSize.width << ","
<< _cascadeParams.winSize.height << "] : "
<< featureEvaluator->getNumFeatures() << "" << endl;
This function just returns numFeatures. See here:
int getNumFeatures() const { return numFeatures; }
For LPB features, this number is computed in generateFeatures:
void CvLBPEvaluator::generateFeatures()
{
int offset = winSize.width + 1;
for( int x = 0; x < winSize.width; x++ )
for( int y = 0; y < winSize.height; y++ )
for( int w = 1; w <= winSize.width / 3; w++ )
for( int h = 1; h <= winSize.height / 3; h++ )
if ( (x+3*w <= winSize.width) && (y+3*h <= winSize.height) )
features.push_back( Feature(offset, x, y, w, h ) );
numFeatures = (int)features.size();
}
Given a contour such as the one seen below, is there a way to get the X,Y coordinates of the top point in the contour? I'm using Python, but other language examples are fine.
Since every pixel needs to be checked, I'm afraid you will have to iterate linewise over the image and see which is the first white pixel.
You can iterate over the image until you encounter a pixel that isn't black.
I will write an example in C++.
cv::Mat image; // your binary image with type CV_8UC1 (8-bit 1-channel image)
int topRow(-1), topCol(-1);
for(int i = 0; i < image.rows; i++) {
uchar* ptr = image.ptr<uchar>(i);
for(int j = 0; j < image.cols; j++) {
if(ptr[j] != 0) {
topRow = i;
topCol = j;
std::cout << "Top point: " << i << ", " << j << std::endl;
break;
}
}
if(topRow != -1)
break;
}
I wanted to binarize low quality images and found that the existing solutions or programs which are implementations of global and local binarization techniques such as Sauvola’s method, NiBlack's method etc are not off much use.
I did find a few good papers regarding much better methods like the ones given in the papers:
1) http://www.ski.org/sites/default/files/publications/wacv11-display-reader.pdf#cite.adap-binar
2) https://www.jstage.jst.go.jp/article/elex/1/16/1_16_501/_pdf
But I haven't worked on image processing much before and so I wanted to know how I could proceed to implement it and what knowledge I need to implement these algorithms
I implemented the binarization of the first paper in like 10 minutes (less time than processing the 2nd image) - no guarantee that it's correct, better have a look at the formulas yourself:
int main()
{
//cv::Mat input = cv::imread("../inputData/Lenna.png");
cv::Mat input = cv::imread("../inputData/LongLineColor.jpg");
cv::Mat gray;
cv::cvtColor(input,gray,CV_BGR2GRAY);
cv::Mat binaryImage = cv::Mat::zeros(gray.rows, gray.cols, CV_8UC1);
// binarization:
// TODO: adjust to your application:
int smallWindowSize = 17; // suggested by the paper
int bigWindowSize = 35; // suggested by the paper
// TODO: adjust to your application
double minTau = 10 ;
// create roi relative to (0,0)
cv::Rect roiTemplate1 = cv::Rect(-smallWindowSize/2,-smallWindowSize/2, smallWindowSize, smallWindowSize);
cv::Rect roiTemplate2 = cv::Rect(-bigWindowSize/2,-bigWindowSize/2, bigWindowSize, bigWindowSize);
cv::Rect imgROI = cv::Rect(0,0, gray.cols, gray.rows);
for(int y=0; y<gray.rows; ++y)
{
std::cout << y << std::endl;
for(int x=0; x<gray.cols; ++x)
{
double pixelThreshold = 255;
// small roi
cv::Rect cROIs = roiTemplate1 + cv::Point(x,y);
// test whether ROI is inside the image. Reduce otherwise:
cROIs = cROIs & imgROI;
if(cROIs.width == 0 || cROIs.height == 0)
continue; // ignore this pixel
// large roi
cv::Rect cROIl = roiTemplate2 + cv::Point(x,y);
cROIl = cROIl & imgROI;
if(cROIl.width == 0 || cROIl.height == 0)
continue; // ignore this pixel
cv::Mat subSmall = gray(cROIs);
cv::Mat subLarge = gray(cROIl);
// evaluate subimages:
// standard deviations
double stdDevS =0;
double stdDevL =0;
// mean value
double meanS =0;
double minL =DBL_MAX;
double meanL =0;
// mean of small region
for(int j=0; j<subSmall.rows; ++j)
for(int i=0; i<subSmall.cols; ++i)
{
meanS += subSmall.at<unsigned char>(j,i);
}
meanS = meanS/ (double)(subSmall.cols*subSmall.rows);
// stddev of small region
for(int j=0; j<subSmall.rows; ++j)
for(int i=0; i<subSmall.cols; ++i)
{
double diff = subSmall.at<unsigned char>(j,i) - meanS;
stdDevS += diff*diff;
}
stdDevS = sqrt(stdDevS/(double)(subSmall.cols*subSmall.rows));
// mean and min of large region
for(int j=0; j<subLarge.rows; ++j)
for(int i=0; i<subLarge.cols; ++i)
{
if(subLarge.at<unsigned char>(j,i) < minL)
{
minL = subLarge.at<unsigned char>(j,i);
meanL += subLarge.at<unsigned char>(j,i);
}
}
meanL = meanL/ (double)(subLarge.cols*subLarge.rows);
// stddef of large region
for(int j=0; j<subLarge.rows; ++j)
for(int i=0; i<subLarge.cols; ++i)
{
double diff = subLarge.at<unsigned char>(j,i) - meanL;
stdDevL += diff*diff;
}
stdDevL = sqrt(stdDevL/(double)(subLarge.cols*subLarge.rows));
// formula (2)
double tau = ((meanS - minL) * (1-stdDevS/stdDevL))/2.0;
// minimum
if(tau < minTau) tau = minTau;
// formula (1)
double Threshold = meanS - tau;
// for debugging:
/*
std::cout << " meanS:" << meanS << std::endl;
std::cout << " std S:" << stdDevS << std::endl;
std::cout << " min L:" << minL << std::endl;
std::cout << " meanL:" << meanL << std::endl;
std::cout << " std L:" << stdDevL << std::endl;
std::cout << " threshold: " << Threshold << std::endl;
*/
unsigned char pixelVal = gray.at<unsigned char>(y,x);
if(pixelVal >= Threshold)
binaryImage.at<unsigned char>(y,x) = 255;
else
binaryImage.at<unsigned char>(y,x) = 0;
}
}
cv::imshow("input", input);
cv::imshow("binary", binaryImage);
//cv::imwrite("../outputData/binaryCustom.png", binaryImage);
cv::waitKey(0);
return 0;
}
giving me these results:
and
It is very slow but not optimized or encapsulated at all ;)
And the results aren't sooo good imho. Probably you have to adjust the windowSizes to your application/task/objectSize
for an ellipse in binary image i applied this code
for(int j=0;j<poi.cols;j++)
{
for(int i=0;i<poi.rows;i++)
{
if(poi.at<uchar>(j,i)==255 && n==0)
{
lx=j;
ly=i;
n=1;
}
}
}
n=0;
for(int j=poi.cols;j>0;j--)
{
for(int i=0;i<poi.rows;i++)
{
if(poi.at<uchar>(j,i)==255 && n==0)
{
rx=j;
ry=i;
n=1;
}
}
}
n=lx;
int y;
cout << lx << "\t" << ly << "\t" << rx << "\t" << ry;
int d=(rx-lx)/5;
for(int i=0;i<5;i++)
{
n=n+d;
y=0;
for(int j=0;j<poi.rows;j++)
{
if(poi.at<uchar>(n,j)>=250 && y==0)
{
cout << "\n" << n << "\t" << j;
y=1;
}
}
}
for finding 10 intermediate points (5 above & 5 below) along the edges. However, I'm not getting correct answer. At times it is not even detecting white pixels
Here's an example binary elipse
EDIT: solved, i had to access the pixel by using
(int)bw.at<uchar>(r,c)
For a project at school i have to detect a ball and calculate it's position. I do this with color segmentation: with inRange i check for the color of the ball and i get a binary image as result. At least i assume it's binary because the picture is black and white when i display it.
I now try to get the position of the ball by simply taking the average of the x and the y coordinates of all the detected pixels. The strange thing is that the y values are correct but the x values are completely wrong.
Here is my code:
int k = 0;
int x = 0;
int y = 0;
ofstream myfile;
myfile.open ("example.txt");
for(int c = 0; c < bw.cols; c++){
for(int r = 0; r < bw.rows; r++){
if(bw.at<int>(r,c) != 0){
x += c;
y += r;
cout << "x: " << c << " y: " << r << endl;
k++;
myfile << 1;
}else{
myfile << 0;
}
}
myfile << endl;
}
myfile.close();
bal.set_pos(x/k,y/k);
I print each pixel's x and y coordinate and the y coordinates are right but the x coordinates are grouped in 4 different groups, the first group has values around 88, second group values around 248, 3rd group 408, 4th group 569. They should be between 350 and 360 however.