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;
}
Im using a C++ openCV program for first principles Algorithm development for HDL(Verilog) image object detection. I've finally managed to get HDL version up to the point of canny detection. In order to validate the two, both need to have identical output. I have found their are subtle differences that I thing are being contributed to by the openCV imread colour to grayscale conversion biasing green. The smoothed image is overall brighter in the openCV C++ method. From looking at the rgb2gray method it appears openCV used a bias ie (RX+GY+B*Z)/3 while in HDL I have been using (R+G+B)/3 as I require it to complete Gaussian, Sobel and Canny filters. Human visualisation is secondary and multiplication by a non-int is undesirable.
Is there a standard linear grayscale conversion for conversion or a means to override the existing method?
...
int main()
{
int thold = 15;
clock_t start;
double duration;
const int sobelX[3][3] = { {-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1} }; //Where origionally floats in python
const int sobelY[3][3] = { {-1, -2, -1}, {0, 0, 0}, {1, 2, 1} }; //Where origionally floats in python
const int kernel[5][5] = { {1,6,12,6,1},
{6,42,79,42,6},
{12,79,148,79,12},
{6,42,79,42,6},
{1,6,12,6,1} };// 1/732
// Above normalised kernal for smoothing, see origional python script for method
start = std::clock();
int height, width, intPixel, tSx, tSy, tS, dirE, dirEE, maxDir, curPoint, contDirection, cannyImgPix, nd, tl, tm, tr, mr, br, bm, bl, ml = 0;
int contNum = 128;
int contPixCount = 0;
int curContNum = 0;
int contPlace = 0;
int oldContPlace = 0;
int g = 0;
bool maxPoint;
struct pixel {
int number;
int h;
int w;
};
std::vector<pixel> contourList;
//double floatPixel = 0.0;
int kernalCumulator = 0;
const int mp = 3;
// Scalar color(0, 0, 255);
// duration = ((clock()) - start) / (double)CLOCKS_PER_SEC;
// start = clock();
// cout << "Start image in" << duration << '\n';
// Mat dst;
Mat rawImg = imread("C:\\Users\\&&&\\Documents\\pycode\\paddedGS.png",0);
printf("%d",rawImg.type());
// Mat rawImg = imread("C:\\Users\\&&&\\Documents\\openCV_Master\\openCVexample\\openCVexample\\brace200.jpg ", 0);
height = rawImg.rows;
width = rawImg.cols;
cout << "Height of image " << height << '\n';
cout << "Width of image " << width << '\n';
Mat filteredImg = Mat::zeros(height, width, CV_8U);
printf("%d", filteredImg.type());
Mat sobelImg = Mat::zeros(height, width, CV_8U);
Mat directionImg = Mat::zeros(height, width, CV_8U);
Mat cannyImg = Mat::zeros(height, width, CV_8U);
Mat contourImg = Mat::zeros(height, width, CV_16U);
// rawImg.convertTo(rawImg, CV_8UC1);
duration = ((clock()) - start) / (double)CLOCKS_PER_SEC;
start = clock();
cout << "Start image in" << duration << '\n';
// Loop to threshold already grayscaled image
/*
for (int h = 0; h < (height); h++)
{
for (int w = 0; w < (width); w++)
{
g = (int)rawImg.at<uchar>(h, w,0);
cout << g << "g";
g+= (int)rawImg.at<uchar>(h, w, 1);
cout << g << "g";
g+= (int)rawImg.at<uchar>(h, w, 2);
cout << g << "g";
g = g/3;
rawGImg.at<uchar>(h,w) = g;
}
}
*/
// imshow("thresholded Image", rawImg);
// waitKey();
// Loop to smooth using Gausian 5 x 5 kernal
// imshow("raw Image", rawImg);
for (int h = 3; h < (height - 3); h++)
{
for (int w = 3; w < (width - 3); w++)
{
if (rawImg.at<uchar>(h, w) >=6 )//Thresholding included
{
for (int xk = 0; xk < 5; xk++)
{
for (int yk = 0; yk < 5; yk++)
{
intPixel = rawImg.at<uchar>((h + (xk - mp)), (w + (yk - mp)));
kernalCumulator += intPixel*(kernel[xk][yk]);//Mutiplier required as rounding is making number go above 255, better solution?
}
}
}
else
kernalCumulator = 0;
kernalCumulator = kernalCumulator / 732;
if (kernalCumulator < 0 || kernalCumulator > 255)
{
// cout << "kernal Value: " << kernalCumulator;
// cout << " intPixel:" << intPixel << '\n';
}
filteredImg.at<uchar>(h, w) = (uchar)kernalCumulator;
kernalCumulator = 0;
}
}
Our vision does not perceive linearly the brightness, so it makes sense for usual applications to use some sort of transformation that tries to mimic the human perception.
For your application, you have 2 options: either use a similar transformation in HDL (which might not be easy or desired), or make a custom rgb to grayscale for OpenCV which uses the same transformation you use.
A short snippet (more like pseudocode, you'll have to figure out the details) for this would be something like:
cv::Mat linearRgbToGray(const cv::Mat &color) {
cv::Mat gray(color.size(), CV_8UC1);
for (int i = 0; i < color.rows; i++)
for (int j = 0; j < color.cols; j++)
gray.at(i, j) = (color.at(i, j)[0] + color.at(i, j)[1] + color.at(i, j)[2]) / 3;
}
As per Paul92's advice above
cv::Mat linearRgbToGray(const cv::Mat &color) {
cv::Mat gray(color.size(), CV_8UC1);
for (int i = 0; i < color.rows; i++)
for (int j = 0; j < color.cols; j++)
gray.at<uchar>(i, j) = ((color.at<cv::Vec3b>(i, j)[0] + color.at<cv::Vec3b>(i, j)[1] + color.at<cv::Vec3b>(i, j)[2]) / 3);
return gray;
}
The above code worked and overcame out of bounds errors I experienced earlier. Thank you, Rob.
I am trying to make an image black and white so i put threshold 100. all the values below 100 will be black and the rest will be white. So i go through every pixel and check its value if it below 100 then i change the value to 0 otherwise i change it to 255. but the code doesn't work. when i print the values of the image. all the value of the image became 225.
Image before run The input image and this is the image after runthe output
int main()
{
int x;
Mat img = imread("Canny.png");
cout << depthToStr(img.depth()) << endl;
img.convertTo(img, CV_32S);
// threshold 100.
for (int z = 0; z < img.rows; z++)
{
for (int y = 0; y < img.cols; y++)
{
if (img.at<int>(z,y) >= 100);
{
img.at<int>(z, y) = 225;
}
}
}
// Print the images.
for (int z = 0; z < img.rows; z++)
{
for (int y = 0; y < img.cols; y++)
{
cout << img.at<int>(z, y) << "\t";
}
cout << endl;
}
img.convertTo(img, CV_8U);
imshow(" ",img);
waitKey(0);
cin >> x;
waitKey(0);
return 0;
}
The if statement has a bug. Remove the semicolon at its end.
if( img.at<int>(z,y) >= 100 ){
img.at<int>(z, y) = 255;
}else{
img.at<int>(z, y) = 0;
}
Note that you most likely don't want to iterate over all pixels because it might not be well optimized for some processors. With opencv you can simply write
img = img > 100;
which would do the same as your cycle.
Another option is opencv function threshold
threshold(img, img, 100, 255, THRESH_BINARY)
I wish to find number of white pixels in every row of binary image. And if that count is greater than 90, I wish to delete the entire row by changing each pixel value in that row to 0. The code that I wrote is not working. And apparently, I am getting the same binary image at output.
Please help me out in fixing the problem. BTW, am using openCV 2.0.
using namespace std;
double a = 15;
double b = 255;
Mat I1;
int main(int argv, char **argc)
{
cv: Mat I = imread("abc.bmp");
if (I.empty())
{
std::cout << "!!! Failed imread(): image not found" << std::endl;
}
threshold(I, I1, a, b, THRESH_BINARY);
int r = I.rows;
int c = I.cols;
for (int j = 0; j < r; j++)
{
int count = 0;
for (int i = 0; i < c; i++)
{
if (I1.at<uchar>(j, i) == 255)
count = count + 1;
}
if (count > 90)
{
for (int i = 0; i < c; i++)
I1.at<uchar>(j, i) = 0;
}
}
namedWindow("Display window", 0);// Create a window for display.
imshow("Display window", I1);
waitKey(0);
return 0;
}
By default imread returns 3 channel BGR image. If you want to load grayscale/binary image use cv::IMREAD_GRAYSCALE parameter:
cv::Mat I = cv::imread("abc.bmp", cv::IMREAD_GRAYSCALE);
I'm new to openCV and C++. I would like to change the pixel values of an image I loaded and display that new image in another window to compare the results (just visually). However, when I run the code, I get two original images. This means that either my for loop isn't doing what's it suppose to do (which i doubt since it makes sense to me) or the pixel value is lost and is not being saved to show the new image. I read a previous post that said I should include this statement after working with each pixel to set in to the altered image. The statement is: img.at(Point(x,y)) = color.
Could somebody please tell me what I'm doing wrong?
Thank you
cv::Mat img = cv::imread("12.jpg", CV_LOAD_IMAGE_COLOR);
// start of pixel navigation
Mat navigateImage(Mat) {
for(int x = 0; x > img.rows; x++)
{
for(int y = 0; y > img.cols; y++){
Vec3b color = img.at<Vec3b>(Point(x,y));
if ( color[0] > 10 && color [1] > 10 && color[2]> 10 )
{
color[0] = 0 ;
color[1] = 0;
color[2] = 0;
img.at<Vec3b>(Point(x,y)) = color;
}
else
{
color.val[0] = 255 ;
color.val[1] = 255;
color.val[2] = 255;
img.at<Vec3b>(Point(x,y)) = color;
}
}
}
return img;
}
// end of pixel navigation
int main( int argc, char** argv )
{
if(! img.data){
cout << "could not open or find the image" << endl;
return -1;}
Mat newImage = navigateImage(img);
cv::imshow( " Original", img);
cv::imshow( " Altered ", newImage);
cv::waitKey(0);
return 0;
}
(1). Firstly,
for(int x = 0; x > img.rows; x++)
and
for(int y = 0; y > img.cols; y++)
should be
for(int x = 0; x < img.cols; x++)
and
for(int y = 0; y < img.rows; y++)
respectively.
Since, you never enter the loop because of this mistake, both images are same.
(2). Secondly,
Mat navigateImage(Mat)
should be
Mat navigateImage(Mat img)
(3). Thirdly, put
cv::Mat img = cv::imread("12.jpg", CV_LOAD_IMAGE_COLOR);
in main function.
(4). Lastly,
replace,
Mat newImage = navigateImage();
by
Mat newImage = navigateImage(img.clone());
else, both images will be same.
CORRECTED CODE -
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
// start of pixel navigation
Mat navigateImage(Mat img) {
for(int x = 0; x < img.cols; x++)
{
for(int y = 0; y < img.rows; y++){
Vec3b color = img.at<Vec3b>(Point(x,y));
if ( color[0] > 10 && color [1] > 10 && color[2]> 10 )
{
color[0] = 0 ;
color[1] = 0;
color[2] = 0;
img.at<Vec3b>(Point(x,y)) = color;
}
else
{
color.val[0] = 255 ;
color.val[1] = 255;
color.val[2] = 255;
img.at<Vec3b>(Point(x,y)) = color;
}
}
}
return img;
}
// end of pixel navigation
int main( int argc, char** argv )
{
Mat img = cv::imread("12.png", CV_LOAD_IMAGE_COLOR);
if(! img.data){
cout << "could not open or find the image" << endl;
return -1;
}
Mat newImage = navigateImage(img.clone());
cv::imshow( " Original", img);
cv::imshow( " Altered ", newImage);
cv::waitKey(0);
return 0;
}