I have a Npp8u * image = new Npp8u[nWidth * nHeight]; array that stores grayscale image values. I defined Npp8u as typedef unsigned char Npp8u;. The problem occurs when I try to subtract the lowest image value from ever pixel.
I use this function to do the subtraction.
void SubtractValue(Npp8u * image, int width, int height, Npp8u value){
int iValue = value;
for (int i = 0; i < height; ++i){
for (int j = 0; j < width; ++j){
int indexVal = (image[i*width + j]);
int newVal = indexVal - iValue;
image[i*width + j] = (Npp8u) newVal;
}
}
}
Result of this function is quite strange. I use Lena Image to test, this is the original image.
If the value is 85 I get the following image
Note: The original image is .pgm
Reading procedure:
for (int i = 0; i < nHeight; ++i)
for (int j = 0; j < nWidth; ++j)
image[i*nWidth + j] = fgetc(fInput);
Writing procedure:
for (int i = 0; i < nHeight; ++i)
for (int j = 0; j < nWidth; ++j)
fputc(image[i*nWidth + j], fOutput);
Update: The problem occurs if the pixel has the value 10, which is new line feed.
Since you mentioned that character 10 was causing the issue, one would suspect that the image format you're reading/writing needs to processed in "binary" file mode, so that ASCII 10 is not treated as a line feed.
Therefore make sure that file is opened in binary mode (for fopen use the "rb" or "wb" flags, depending on whether you're reading or writing).
Related
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 am working with C++ and OpenCV on Visual Studio; for my application, I have a set of images and a .mask one that permits to select a ROI in each of them (it is 0 everywhere but in the ROI).
I load the images (duck1.jpg, duck2.jpg, etc.) with:
std::array<cv::Mat, n_imgs> img;
std::string folder = "C:/Users/imgs/";
std::string fname = "duck";
std::string format = ".jpg";
for (int i = 0; i < n_imgs; i++)
img[i] = cv::imread(folder + fname + std::to_string(i + 1) + format, 0);
Then, I apply a mask:
cv::Mat mask = cv::imread(folder + fname + ".mask" + format, 0);
for (int i = 0; i < img[0].rows; i++)
for (int j = 0; j < img[0].cols; j++)
for (int k = 0; k < n_imgs; k++)
if (mask.at<float>(i, j) == 0)
img[k].at<float>(i, j) = 0.f;
I keep getting 'Project.exe has triggered a breakpoint.' quite randomly in some recurring points of my successive code (which I won't post here because is quite long); however, these problems disappear when I comment the masking line.
Given the symptoms, I supposed it is an allocation problem, am I right? How can I fix it?
It will be more of a guess what is wrong but I will give you a hint.
Are you sure that img have float as underlying type? Because when you do cv::imread(file, IMREAD_GRAYSCALE ) this 0 stand for gray scale image which usually is CV_8UC1 (unsigned char of 8 bit) when you address it with float (which has size of 32 bit) you may end up writing memory after the end of image (24 bits after last pixel in image are written). This may sometimes trigger error and sometimes not, it depends on what is in memory after your image (is it allocated or not). I guess that if you run your program in Debug mode it will always fail.
So change:
for (int i = 0; i < img[0].rows; i++)
for (int j = 0; j < img[0].cols; j++)
for (int k = 0; k < n_imgs; k++)
if (mask.at<float>(i, j) == 0)
img[k].at<float>(i, j) = 0.f;
To:
for (int i = 0; i < img[0].rows; i++)
for (int j = 0; j < img[0].cols; j++)
for (int k = 0; k < n_imgs; k++)
if (mask.at<unsigned char>(i, j) == 0)
img[k].at<unsigned char>(i, j) = 0;
If you want your code to run faster and you have binary mask (with values of 0 and 1) you can just multiply these element-wise like this (note that this is only for one image):
cv::Mat afterApplyMask = img.mul(mask);
I was writing a C code in Visual Studio when I encountered this error. My project name and name of the solution in the source file was the same. Once I changed those and debugged, I stopped encountering the error.
Sorry for putting it out here but opencv site is down or something i cannot access the topic there. I am reading a RGB image in Mat and than trying to convert in into BYTE* using the following function but it is not giving me the exact image but distorted part of some of the image. Please can anyone help me out here where i am mistaking it. Thank you.
BYTE *change_to_BYTE(Mat matrix_value)
{
BYTE* v_char_new = new BYTE[matrix_value.rows * matrix_value.cols * 3]();
vector<byte> v_char;
for(int i = 0; i < matrix_value.rows; i++)
{
for(int j = 0; j < matrix_value.cols; j++)
{
v_char_new[((i*matrix_value.cols+j) * 3)+0] = (*(uchar*)((matrix_value.data+ i*matrix_value.step + j + 0)));
v_char_new[((i*matrix_value.cols+j) * 3)+1] = (*(uchar*)((matrix_value.data+ i*matrix_value.step + j +1)));
v_char_new[((i*matrix_value.cols+j) * 3)+2] = (*(uchar*)((matrix_value.data+ i*matrix_value.step + j +2)));
}
}
return v_char_new;
}
After checking it out in different ways this was the one that worked for me.
BYTE *Ctest_face_projectDlg::change_to_BYTE(Mat matrix_value)
{
BYTE* v_char_new = new BYTE[matrix_value.rows * matrix_value.cols * 3]();
for(int i = 0; i < matrix_value.rows; i++)
{
for(int j = 0; j < matrix_value.cols; j++)
{
Vec3b bgrPixel = matrix_value.at<Vec3b>(i, j);
v_char_new[((i*matrix_value.cols+j)*3)+0] = bgrPixel.val[0];
v_char_new[((i*matrix_value.cols+j)*3)+1] = bgrPixel.val[1];
v_char_new[((i*matrix_value.cols+j)*3)+2] = bgrPixel.val[2];
// do something with BGR values...
}
}
}
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.
I'm trying to change a for loop's start variable to use an if statement instead. However, they don't seem to be equivalent..
I have a loop that looked like:
int K = 0;
std::uint32_t CheckSum = 0;
const std::uint8_t* BuffPos = static_cast<const std::uint8_t*>(Data);
int Start = Height < 12 ? 1 : 12;
for (std::size_t I = Start; I < Height; ++I)
{
for (std::size_t J = 0; J < Width; ++J, ++K)
{
BuffPos += 3; //skip RGB and move to alpha pixel.
CheckSum += *(BuffPos++); //Checksum = count of alpha pixels.
}
}
std::cout<<CheckSum;
However, I don't want my loop to start with I = Start.
I'd rather do that in an if statement. I tried the following:
int K = 0;
std::uint32_t CheckSum = 0;
const std::uint8_t* BuffPos = static_cast<const std::uint8_t*>(Data);
int Start = Height < 12 ? 1 : 12;
for (std::size_t I = 0; I < Height; ++I)
{
for (std::size_t J = 0; J < Width; ++J, ++K)
{
BuffPos += 3; //Skip RGB and move to alpha pixel.
if (I >= Start) //The if statement.
CheckSum += *(BuffPos++); //Checksum = count of alpha pixels.
}
}
std::cout<<CheckSum;
However, the second version with the if statement prints a totally different number than the first for the exact same bitmap.
Is there a reason why it does this? How can I fix it? I can't see why it would be any different :S
As you noticed, it is no longer the same.
Sure, you segregated the checksum bit, but now you are incrementing BufPos a different number of times. Your loop is now iterating more times, so BuffPos has a different value once you start creating the checksum than it did in the first version.
Look at it this way; the first version starts accumulating the checksum at BuffPos + 3. The second version begins accumulating the checksum from BuffPos + (Start * Width) + 3.
Honestly, the first version is better. I can't tell you which one is correct, but assuming the first version is (iterating from Start) then why introduce a branch at all? It just muddles up the code.
If you want the second version to be the same as the first you'll need to initialize BuffPos to Data + Start * Width or place both statements within the if. Of course, if you do that, you're just doing nothing at all until I == Start, and that should tell you something.
how's this version?
int K = 0;
std::uint32_t CheckSum = 0;
const std::uint8_t* BuffPos = static_cast<const std::uint8_t*>(Data);
int Start = Height < 12 ? 1 : 12;
for (std::size_t I = 0; I < Height; ++I)
{
if (I < Start) //The if statement.
continue;
for (std::size_t J = 0; J < Width; ++J, ++K)
{
BuffPos += 3; //Skip RGB and move to alpha pixel.
CheckSum += *(BuffPos++); //Checksum = count of alpha pixels.
}
}
std::cout<<CheckSum;
Both statements(Buffpos and cheksum) should come under if statement .