Interchange loop variable with if statement - c++

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 .

Related

Traverse Mat as 64x64 per iteration

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);

Faster image mirroring algorithm

I already know how to flip an image vertically or horizontally. I have the following code that does so horizontally. The image data here is stored in a QImage as I was working with Qt here.
QImage image(imageFileName);
QImage newImage(image);
if(image.depth() > 8)
{
for (int idx_Y = 0; idx_Y < image.height(); idx_Y++)
{
for (int idx_X = 0; idx_X < image.width(); idx_X++)
{
QRgb rgb = image.pixel(image.width() - 1 - idx_X, idx_Y);
newImage.setPixel(idx_X, idx_Y, rgb);
}
}
}
I'm sure there are faster methods to get it done. However, I don't want any memory allocation on the heap. Could you please tell me what other much faster algorithms there could be?
Thank you.
Elaborating on #Spektres hint
2 nested for loops are not the problem... the setPixel and pixel functions are usually crawlingly slooow on most gfx APIs. Using direct pixel access instead usually boost speed ~1000 times or more ...
This could look like:
QImage image(imageFileName);
QImage newImage(image);
if (image.depth() >= 8) {
const int bytesPerPixel = image.depth() / 8;
for (int y = 0; y < image.height(); ++y) {
char *dataSrc = image.bits() + y * image.bytesPerLine();
char *dataDst = newImage.bits() + y * newImage.bytesPerLine()
+ (newImage.width() - 1) * bytesPerPixel;
for (int i = image.width(); i--;
dataSrc += bytesPerPixel, dataDst -= bytesPerPixel) {
for (int i = 0; i < bytesPerPixel; ++i) dataDst[i] = dataSrc[i];
}
}
}
Please, note that I changed image.depth() > 8 into image.depth() >= 8. (I saw no reason to exclude e.g. QImage::Format_Grayscale8.)
A slightly modified version for mirroring the QImage newImage in-place (considering that it is already copied):
QImage image(imageFileName);
QImage newImage(image);
if (newImage.depth() >= 8) {
const int bytesPerPixel = newImage.depth() / 8;
for (int y = 0; y < image.height(); ++y) {
char *dataL = newImage.bits() + y * newImage.bytesPerLine();
char *dataR = dataL + (newImage.width() - 1) * bytesPerPixel;
for (; dataL < dataR; dataL += bytesPerPixel, dataR -= bytesPerPixel) {
for (int i = 0; i < bytesPerPixel; ++i) std::swap(dataL[i], dataR[i]);
}
}
}
Concerning QImage and qRgb(), you may also notice that Qt supports QImages with 16 bits per component (since Qt 5.12).
I fiddled a bit with this in
SO: Set pixel value of 16 bit grayscale QImage
which might be interesting as well.

grayscale Laplace sharpening implementation

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.

'Project.exe has triggered a breakpoint.'

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.

Array index seems to slide when writing values

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).