I'm trying to get BGR values from a streaming webcam image. I'm getting a memory access violation because I'm not using the pointer correctly in the nested for loop but I don't know what the syntax should be. I can't find documentation that is specific enough to the seemingly basic task I'm trying to do.
In addition to solving he memory access violation, I want to also be able to edit each pixel on the fly without having to do a deep copy but don't know what he syntax should be for that also.
This is the code I have so far:
int main(int argc, char** argv)
{
int c;
Mat img;
VideoCapture capture(0);
namedWindow("mainWin", CV_WINDOW_AUTOSIZE);
bool readOk = true;
while (capture.isOpened()) {
readOk = capture.read(img);
// make sure we grabbed the frame successfully
if (!readOk) {
std::cout << "No frame" << std::endl;
break;
}
int nChannels = img.channels();
int nRows = img.rows;
int nCols = img.cols * nChannels;
if (img.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
int i, j;
uchar r, g, b;
for (i = 0; i < nRows; ++i)
{
for (j = 0; j < nCols; ++j)
{
r = img.ptr<uchar>(i)[nChannels*j + 2];
g = img.ptr<uchar>(i)[nChannels*j + 1];
b = img.ptr<uchar>(i)[nChannels*j + 0];
}
}
if (!img.empty()) imshow("mainWin", img);
c = waitKey(10);
if (c == 27)
break;
}
}
Your scanning loop is not correct. You should be only getting a pointer to the row once per row.
Since pixels are 3 byte quantities, it is easiest to treat them as a Vec3b.
You should have something like
uchar r, g, b;
for (int i = 0; i < img.rows; ++i)
{
cv::Vec3b* pixel = img.ptr<cv::Vec3b>(i); // point to first pixel in row
for (int j = 0; j < img.cols; ++j)
{
r = pixel[j][2];
g = pixel[j][1];
b = pixel[j][0];
}
}
OR
uchar r, g, b;
for (int i = 0; i < img.rows; ++i)
{
uchar* pixel = img.ptr<uchar>(i); // point to first color in row
for (int j = 0; j < img.cols; ++j)
{
b = *pixel++;
g = *pixel++;
r = *pixel++;
}
}
NOTE
It is fairly common to see Mat::at() used to access pixels sequentially like:
// DON'T DO THIS!
uchar r, g, b;
for (int i = 0; i < img.rows; ++i)
{
for (int j = 0; j < img.cols; ++j)
{
cv::Vec3b pixel = img.at<cv::Vec3b>(i, j);
r = pixel[2];
g = pixel[1];
b = pixel[0];
}
}
However such uses are inappropriate.
For every pixel access, at() needs to calculate an index by multiplying the row number and row length - and over a whole image that calculation can result in processing times considerably slower than with the code above (where ptr() does an equivalent calculation once per row.
Furthermore, in debug mode at() has an assertion that makes it much slower again.
If you are sure there is no padding between rows, it is possible to go faster by eliminating the call to ptr(). In this case the pixel pointer in the second loop above will after the end of each line be pointing at the start of the next line. But that wont work if your Mat is for example some region of interest of some other Mat.
On the other hand, if you were accessing pixels in a random fashion, rather than scanning sequentially like above, at() is then very appropriate.
Related
Good day,
I am looking for a nested for loop to traverse the image of size 512x512 as 64x64 per iteration. My goal is to determine the element of each sub-region, such as performing number of edge count.
In this following code, I have tried to iterate per 64 row and 64 col (expect 8 times each to hit 512). Within the nested for loop, I have placed vec3b as a test run and I aware that the entire cycle of my code is repeating an identical pattern rather than traverse entire image.
int main()
{
char imgName[] = "data/near.jpg"; //input1.jpg, input2.jpg, near.jpg, far.jpg
Mat sourceImage = imread(imgName);
resize(sourceImage, sourceImage, Size(512, 512));
for (int t_row = 0; t_row < sourceImage.rows; t_row += 64)
{
for (int t_col = 0; t_col < sourceImage.cols; t_col += 64)
{
for (int row = 0; row < 64; row++)
{
for (int col = 0; col < 64; col++)
{
Vec3b bgrPixel = sourceImage.at<Vec3b>(row, col);
cout << bgrPixel << endl;
}
}
}
}
return 0;
}
If you actually want to have 64x64 sub-images per iteration, make use of OpenCV's Rect, like so:
const int w = 64;
const int h = 64;
for (int i = 0; i < int(sourceImage.size().width / w); i++)
{
for (int j = 0; j < int(sourceImage.size().height / h); j++)
{
cv::Mat smallImage = sourceImage(cv::Rect(i * w, j * h, w, h));
// Pass smallImage to any function...
}
}
You are iterating over
Vec3b bgrPixel = sourceImage.at<Vec3b>(row, col);
with 0 <= row < 64 and 0 <= col < 64. You are right that you iterate 64 times over the same region.
It should be
Vec3b bgrPixel = sourceImage.at<Vec3b>(t_row + row, t_col + col);
I am trying to implement Laplace sharpening using C++ , here's my code so far:
img = imread("cow.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
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 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++) {
res.at<uchar>(i,j) += filter[h - i][w - j] * img.at<uchar>(h,w);
}
}
}
}
//img - laplace
for (int y = 0; y < res.rows; y++) {
for (int x = 0; x < res.cols; x++) {
res.at<uchar>(y, x) = img.at<uchar>(y, x) - res.at<uchar>(y, x);
}
}
return res;
}
I don't really know what went wrong, I also tried different filter (1,1,1),(1,-8,1),(1,1,1) and the result is also same (more or less). I don't think that I need to normalize the result because the result is in range of 0 - 255. Can anyone explain what really went wrong in my code?
Problem: uchar is too small to hold partial results of filerting operation.
You should create a temporary variable and add all the filtered positions to this variable then check if value of temp is in range <0,255> if not, you need to clamp the end result to fit <0,255>.
By executing below line
res.at<uchar>(i,j) += filter[h - i][w - j] * img.at<uchar>(h,w);
partial result may be greater than 255 (max value in uchar) or negative (in filter you have -4 or -8). temp has to be singed integer type to handle the case when partial result is negative value.
Fix:
for (i = 0; i < newImageHeight; i++) {
for (j = 0; j < newImageWidth; j++) {
int temp = res.at<uchar>(i,j); // added
for (h = i; h < i + filterHeight; h++) {
for (w = j; w < j + filterWidth; w++) {
temp += filter[h - i][w - j] * img.at<uchar>(h,w); // add to temp
}
}
// clamp temp to <0,255>
res.at<uchar>(i,j) = temp;
}
}
You should also clamp values to <0,255> range when you do the subtraction of images.
The problem is partially that you’re overflowing your uchar, as rafix07 suggested, but that is not the full problem.
The Laplace of an image contains negative values. It has to. And you can’t clamp those to 0, you need to preserve the negative values. Also, it can values up to 4*255 given your version of the filter. What this means is that you need to use a signed 16 bit type to store this output.
But there is a simpler and more efficient approach!
You are computing img - laplace(img). In terms of convolutions (*), this is 1 * img - laplace_kernel * img = (1 - laplace_kernel) * img. That is to say, you can combine both operations into a single convolution. The 1 kernel that doesn’t change the image is [(0,0,0),(0,1,0),(0,0,0)]. Subtract your Laplace kernel from that and you obtain [(0,-1,0),(-1,5,-1),(0,-1,0)].
So, simply compute the convolution with that kernel, and do it using int as intermediate type, which you then clamp to the uchar output range as shown by rafix07.
I have two matrices:
cv::Mat bgr(rows, cols, CV_16UC3);
cv::Mat ir(rows, cols, CV_16UC1 );
and I want to subtract ir from each channel of bgr element-wise. I couldn't find an elegant solution yet.
EDIT
One possible solution might be:
// subtract IR from BGR
Vec3u tmp;
for (int i = 0; i < ir.rows; i++) {
for (int j = 0; j < ir.cols; j++) {
tmp = bgr.at<Vec3u>(i,j);
tmp[0] = tmp[0] - ir.at<ushort>(i,j);
tmp[1] = tmp[1] - ir.at<ushort>(i,j);
tmp[2] = tmp[2] - ir.at<ushort>(i,j);
bgr.at<Vec3u>(i, j) = tmp;
}
}
The question is that whether there is a faster solution.
If we're talking about an elegant way, it could be like this:
Mat mat = Mat::ones(2,2,CV_8UC1);
Mat mat1 = Mat::ones(2,2,CV_8UC2)*3;
Mat mats[2];
split(mat1,mats);
mats[0]-=mat;
mats[1]-=mat;
merge(mats,2,mat1);
You shouldn't use at(), if you wanted your code to be more efficient. Use pointers and check Mats for continuity:
int rows = mat.rows;
int cols = mat.cols;
if(mat.isContinuous() && mat1.isContinuous())
{
cols*=rows;
rows = 1;
}
for(int j = 0;j<rows;j++) {
auto channe2limg = mat1.ptr<Vec2b>(j);
auto channelimg = mat.ptr<uchar>(j);
for (int i = 0; i < cols; i++) {
channe2limg[i][0]-=channelimg[i];
channe2limg[i][1]-=channelimg[i];
}
}
I have utilised the OpenCV GrabCut functionality to perform an image segmentation. When viewing the segmented image as per the code below, the segmentation is reasonable/correct. However, when looking at(at attempting to use) the segmrntation mask values, I am getting some very large numbers, and not the enumerated values one would expect from the cv::GrabCutClasses enum.
void doGrabCut(){
Vector2i imgDims = getImageDims();
//Wite image to OpenCV Mat.
const Vector4u *rgb = getRGB();
cv::Mat rgbMat(imgDims.height, imgDims.width, CV_8UC3);
for (int i = 0; i < imgDims.height; i++) {
for (int j = 0; j < imgDims.width; j++) {
int idx = i * imgDims.width + j;
rgbMat.ptr<cv::Vec3b>(i)[j][2] = rgb[idx].x;
rgbMat.ptr<cv::Vec3b>(i)[j][1] = rgb[idx].y;
rgbMat.ptr<cv::Vec3b>(i)[j][0] = rgb[idx].z;
}
}
//Do graph cut.
cv::Mat res, fgModel, bgModel;
cv::Rect bb(bb_begin.x, bb_begin.y, bb_end.x - bb_begin.x, bb_end.y - bb_begin.y);
cv::grabCut(rgbMat, res, bb, bgModel, fgModel, 10, cv::GC_INIT_WITH_RECT);
cv::compare(res, cv::GC_PR_FGD, res, cv::CMP_EQ);
//Write mask.
Vector4u *maskPtr = getMask();//uchar
for (int i = 0; i < imgDims.height; i++) {
for (int j = 0; j < imgDims.width; j++) {
cv::GrabCutClasses classification = res.at<cv::GrabCutClasses>(i, j);
int idx = i * imgDims.width + j;
std::cout << classification << std::endl;//Strange numbers here.
maskPtr[idx].x = (classification == cv::GC_PR_FGD) ? 255 : 0;//This always evaluates to 0.
}
}
cv::Mat foreground(rgbMat.size(), CV_8UC3, cv::Scalar(255, 255, 255));
rgbMat.copyTo(foreground, res);
cv::imshow("GC Output", foreground);
}
Why would one get numbers outside the enumeration when the segmentation is qualitatively correct?
I doubt on your //Write mask. step, why do you re-iterate the res and modify maskPtr as maskPtr[idx].x = (classification == cv::GC_PR_FGD) ? 255 : 0;, Basically you already have a single channel Binary image stored in the res variable, the cv::compare() returns a binary image
However if you still want to debug the values by iteration then you should use the standard technique for iterating a single channel image as:
for (int i = 0; i < m.rows; i++) {
for (int j = 0; j < m.cols; j++) {
uchar classification = res.at<uchar>(i, j);
std::cout << int(classification) << ", ";
}
}
As you are iterating a single channel mat you must use res.at<uchar>(i, j) and not res.at<cv::GrabCutClasses>.
Why does the assertion fail here when i create a CvMat *? It does not happen with an image i load in cv::Mat using a pointer.
struct RGB { unsigned char b, g, r; };
cv::Point p;
RGB *data;
CvMat* mat = cvCreateMat(300,300,CV_32FC1);
for( row = 0; row < mat->rows; ++row)
{
for ( col = 0; col < mat->cols; ++col)
{
p.x=row,p.y=col;
ERROR ----->>> assert((mat->step/mat->cols) == sizeof(RGB));
data = (RGB*)&mat->data;
data += p.y * mat->cols + p.x;
}
}
For this code the assertion does not fail:
IplImage * img=cvLoadImage("blah.jpg");
int row=0,col=0;
cv::Mat in(img);
cv::Mat *mat=∈
cv::Point p;
struct RGB { unsigned char b, g, r; };
RGB *data;
for( row = 0; row < mat->rows; ++row)
{
for ( col = 0; col < mat->cols; ++col)
{
p.x=row,p.y=col;
assert((mat->step/mat->cols) == sizeof(RGB));
data = (RGB*)&mat->data;
data += p.y * mat->cols + p.x;
printf("Row=%dxCol=%d b=%u g=%u r=%u\n",row,col,data->b,data->g,data->r);
wait_for_frame(1);
}
}
Because sizeof(RGB) != sizeof(float), which is what you filled the matrix with here:
CvMat* mat = cvCreateMat(300,300,CV_32FC1);
CV_32FC1 means 1 component, 32-bit floating point. You probably want CV_8UC3. See here or another OpenCV reference.
You can skip the entire IplImage misery if you use
cv::Mat img = cv::loadImage("blah.jpg");
Also it is better to use row ptr for going through all the pixels.
It knows the jumps, so you don't have to worry!
From the refman:
If you need to process a whole row of a 2D array, the most efficient
way is to get the pointer to the row first, and then just use the
plain C operator []
Be aware that if you are loading bigger images which have "jumps" in their data, your code will not work.
In your situation
cv::Mat img = cv::loadImage("blah.jpg");
const cv::Mat& M = img;
for(int i = 0; i < rows; i++)
{
const Vec3b* Mi = M.ptr<Vec3b>(i);
for(int j = 0; j < cols; j++)
{
const Vec3b& Mij = Mi[j];
std::cout<<"Row="<<i<<"Col="<<j<<"\t";
std::cout<<"b="<<Mij[0]<<" g="<<Mij[1]<<" r="<<Mij[2]<<std::endl;
}
}
is the fastest correct way. Otherwise you could use M.at<Vec3b>(i,j).