I have read quite a few articles in SO and cplusplus.com and decided to give a try to the flattened, 1D array that mimics 2D and 3D.
I managed to get a prototype to work with some values, but there is something wrong with the indices, which has to be the formula. All I did was copy the formula from different places and applied to the code. Here it is:
#include <iostream>
using namespace std;
int main(void)
{
float *flat_2d_array, *flat_3d_array;
int width, height, depth, counter;
counter = 1;
width = 2;
height = 3;
depth = 4;
flat_2d_array = new float[width * height];
flat_3d_array = new float[width * height * depth];
// 2D part, works fine
for(int x = 0; x < width; x++)
for(int y = 0; y < height; y++)
flat_2d_array[y * width + x] = counter++;
for(int x = 0; x < width; x++)
for(int y = 0; y < height; y++)
cout << "Element [" << x << "]" << "[" << y << "] = " << flat_2d_array[y * width + x] << endl;
cout << endl;
// Resets the counter and runs the 3D part
counter = 1;
for(int x = 0; x < width; x++)
for(int y = 0; y < height; y++)
for(int z = 0; z < depth; z++)
flat_3d_array[z * height * depth + y * depth + x] = counter++;
for(int x = 0; x < width; x++)
for(int y = 0; y < height; y++)
for(int z = 0; z < depth; z++)
cout << "Element [" << x << "]" << "[" << y << "]" << "[" << z << "] = " << flat_3d_array[z * height * depth + y * depth + x] << endl;
delete[] flat_2d_array;
delete[] flat_3d_array;
return 0;
}
It just declares a few variables, allocates memory for the arrays, populates them with a counter in for() loops and prints the elements, then frees the memory.
If you copy/paste it will compile the way it is and will run fine.
However, if you change width to 3 and height to 2, then compile and run, it will crash after the element [2][1][3] in the 3D part.
So there seems to be indexing problem with this formula I'm using for the 3D:
3d_array[ X ][ Y ][ Z ] == flat_3d_array[ Z * height * depth + Y * depth + X ]
Can you guys see anything incorrect?
Can you guys see anything incorrect?
yes, your formula should be instead:
z * height * width + y * width + x
or more efficient form:
( z * height + y ) * width + x
and you should make loop on x inner, otherwise you iterate against CPU cache.
To make clear: making loop on x inner would not affect correctness of your program, it will make it more efficient (including iteration on 2d array as well). Your program crashes because you calculate linear index for 3d array by wrong formula.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 months ago.
Improve this question
I am trying to make a height map using randomly generated values in console using c++.
I have an array [50, 50] that is initialized as all 0s.
matrix = new int[rows * columns];
for (int i = 0; i < rows; i++) {
for (int k = 0; k < columns; k++) {
matrix[i * columns + k] = 0;
}
}
I use a loop to generate random points in the array and store the position of those points in a vector.
std::vector<int> sprinklePeaks(int peakDensity, int dimension) {
int count = 0;
std::vector<int> peaks;
for (int i = 0; i < peakDensity; i++) {
int peakIndex = randomize(0, dimension);
matrix[peakIndex] = randomize(69, 99);
peaks.push_back(peakIndex);
}
return peaks;
}
Problem: I am trying to generate a circle with a random radius around these points and fill the circle with randomly generated values, increasing as they approach the center, my circle generation seems to have the right coordinates for the center but the distance is wrong and no values get added to the matrix.
void circleGen(std::vector<int> peaks, int dimensions) {
for (int i = 0; i < peaks.size(); i++) {
int radius = randomize(5, 15);
int area = 3.14159 * radius * radius;
int index = peaks[i];
int x = index / 50;
int y = index % 50;
// std::cout << "Peak: " << peaks[i] << "\n";
// std::cout << "Peak Coordinates: [" << x << ", " << y << "]\n";
int peakHeight = matrix[peaks[i]];
for (int k = 0; k < radius * 2; k++) {
for (int j = 0; j < radius * 2; j++) {
int distance = sqrt((i - radius) * (i - radius) + (j - radius) * (j - radius));
matrix[x - j * columns + y - k] = randomize(10, 20);
}
}
}
}
First issue could be that you don't have bounds checking. I assume you should not change the matrix (and more importantly the surrounding memory) if coordinates are out of the bounds.
Second issue I see is that you calculate distance, but then you don't use this value.
Third issue could be that you want to add to the matrix cell, not replace the value. In line matrix[x - j * columns + y - k] = randomize(10, 20); maybe it should be += instead of =. And distance should participate in the right-side. Or you want to replace, but only if new value is greater than the existing value.
Fourth, as Andrej in the comments mentioned, the math to calculate the index in the matrix is wrong. Should use parentheses, (x - j) * columns + y - k
I was trying to implement convolute2D (filter2D in OpenCV) and came up with the following code.
Mat convolute2D(Mat image, double** kernel, int W){
Mat filtered_image = image.clone();
// find center position of kernel (half of kernel size)
int kCenterX = W / 2;
int kCenterY = W / 2;
int xx = 0;
int yy = 0;
cout << endl << "Performing convolution .." << endl;
cout << "Image Size : " << image.rows << ", " << image.cols <<endl;
for (int i = 0; i < image.rows; ++i){
for (int j = 0; j < image.cols; ++j){
for(int x = 0; x < W; ++x){
xx = W - 1 - x;
for(int y = 0; y < W; ++y){
yy = W - 1 - y;
int ii = i + (x - kCenterX);
int jj = j + (y - kCenterY);
if( ii >= 0 && ii < image.rows && jj >= 0 && jj < image.cols) {
filtered_image.at<uchar>(Point(j, i)) += image.at<uchar>(Point(jj, ii)) * kernel[xx][yy];
}
}
}
}
}
return filtered_image;
}
Assuming we always have a square kernel. But my results have been much different from filter2D. Is it because of possible overflow or is there a problem with my implementation?
Thanks
There are two issues with your code:
You don't set the output image to zero before adding values to it. Consequently, you are computing "input + filtered input", rather than just "filtered input".
Presuming that kernel has quite small values, "input pixel * kernel value" will likely yield a small number, which is rounded down when written to a uchar. Adding up each of these values for the kernel, you'll end up with a result that is too low.
I recommend that you do this:
double res = 0;
for(int x = 0; x < W; ++x){
int xx = W - 1 - x;
for(int y = 0; y < W; ++y){
int yy = W - 1 - y;
int ii = i + (x - kCenterX);
int jj = j + (y - kCenterY);
if( ii >= 0 && ii < image.rows && jj >= 0 && jj < image.cols) {
res += image.at<uchar>(Point(jj, ii)) * kernel[xx][yy];
}
}
}
filtered_image.at<uchar>(Point(j, i)) = res;
This solves both issues at once. Also, this should be a bit faster because accessing the output image has a bit of overhead.
For much faster speeds, consider that the check for out-of-bounds reads (the if in the inner loop) slows down your code significantly, and is totally unnecessary for most pixels (as few pixels are near the image edge). Instead, you can split up your loops into [0,kCenterX], [kCenterX,image.rows-kCenterX], and [image.rows-kCenterX,image.rows]. The middle loop, which is typically by far the largest, will not need to check for out-of-bounds reads.
And use cv::saturate_cast for correct assignment to uchar, for example:
filtered_image.at<uchar>(Point(j, i)) = cv::saturate_cast<uchar>(res);
I have two grey scale images in txt files, one being a smaller block of the Main image. I have read the images into two different 2d vector matrices.
The Rows and the Columns of the images are:
Main: M = 768 N = 1024
SubImg: R = 49 C = 36
int R = 49; int C = 36; //Sub Image Rows / Columns
int M = 768; int N = 1024; //Main Image Rows / Columns
I want to loop through the Main image by blocks of width: 49 and height: 36 and put each block into an array, so I can compare the array with the Sub image (using Nearest Neighbor Search) to see which block has the closest result to the Sub image.
The problem I am having is that I cannot get the loop to display all of the blocks. When I run the loop only a certain number of block appear and the program clashes.
// Testing Main 2D Vector in block format
for (int bx = 0; bx < M; bx += R)
for (int by = 0; by < N; by += C)
{
for (int x = 0; x < R; ++x)
{
for (int y = 0; y < C; ++y)
{
cout << MainIMG_2DVector[bx + x][by + y] << " ";
}
}
cout << "\n\n" << endl;
}
Can someone please tell me what I have done wrong.
Thanks
EDIT +++++++++++++++++++++++++++++++++++++++++
After debugging
_DEBUG_ERROR("vector subscript out of range");
_SCL_SECURE_OUT_OF_RANGE;
M=768 is not divisible by R=49, the last loop starts with bx=735 (15*49) and should ends to bx=735+48=783 > 768... Same problem in N=1024 and C=36 by=1008 (28*36) to by=1008+35=1043 > 1024. – J. Piquard
If I increase the width and the height, my main image stretch. Is there a way around this?
Two ways could be explored:
Way 1 - change the value R (and C) to the best divider of M (and N)
int M = 768; int N = 1024; //Main Image Rows / Columns
int R = 48; int C = 32; //Sub Image Rows (768=16*48) / Columns (1024=32*32)
Way 2 - prevent out of range error in the for-loop exit condition
For x, both conditions (x < R) and ((bx + x) < M)) shall be
true.
And for y, both conditions (y < C) and ((by + y) < N)) shall be
true.
for (int x = 0; ((x < R)&&((bx + x) < M)); ++x)
{
for (int y = 0; ((y < C)&&((by + y) < N)); ++y)
{
if ((bx + x)>=M) {
std::cout << (bx + x) << (by + y) << " ";
}
}
}
Instead of:
for (int x = 0; x < R; ++x)
{
for (int y = 0; y < C; ++y)
{
if ((bx + x)>=M) {
std::cout << (bx + x) << (by + y) << " ";
}
}
}
To preface this: I'm currently a first-year student who was allowed to enroll in some second-year classes. Because of this, I'm currently wrangling a language (C++) that I haven't really had the time to learn (First-years mostly learn C#), so this code might not be pretty.
Our assignment is twofold. First, we need to write a program that outputs a Mandelbrot image in a PPM. To achieve this, I've followed a Youtube tutorial here.
The second part of the assignment is to make the program multithreaded. Essentially, the program is supposed to use 4 threads that each draw a quarter of the image.
To this end I have altered the code from the video tutorial, and converted the main to a method. Now, I'm trying to properly make the first quarter of the image. I figured the way to do this was to adjust
for (int y = 0; y < imageHeight; y++) //Rows!
{
for (int x = 0; x < imageWidth; x++) //Columns! (pixels in every row)
{
to
for (int y = 0; y < halfHeight; y++) //Rows!
{
for (int x = 0; x < halfWidth; x++) //Columns! (pixels in every row)
{
However, instead of drawing the top left quarter as I suspected, the program drew along the full width, repeating itself after the halfway mark of the image width was reached, and only drew along a quarter of the height
(see image)
As I like to learn from my mistakes, I'd love to know what exactly is going wrong here.
Thank you for helping a programming greenhorn out :)
Full program code below.
#include "stdafx.h"
#include <fstream>
#include <iostream>
int imageWidth = 512, imageHeight = 512, maxN = 255, halfWidth = 256, halfHeight = 256;
double minR = -1.5, maxR = 0.7, minI = -1.0, maxI = 1.0;
std::ofstream f_out("output_image.ppm");
int findMandelbrot(double cr, double ci, int max_iterations)
{
int i = 0;
double zr = 0.0, zi = 0.0;
while (i < max_iterations && zr * zr + zi * zi < 4.0)
{
double temp = zr * zr - zi * zi + cr;
zi = 2.0 * zr * zi + ci;
zr = temp;
i++;
}
return i;
}
double mapToReal(int x, int imageWidth, double minR, double maxR)
{
double range = maxR - minR;
return x * (range / imageWidth) + minR;
}
double mapToImaginary(int y, int imageHeight, double minI, double maxI)
{
double range = maxI - minI;
return y * (range / imageHeight) + minI;
}
void threadedMandelbrot()
{
for (int y = 0; y < halfHeight; y++) //Rows!
{
for (int x = 0; x < halfWidth; x++) //Columns! (pixels in every row)
{
//... Find the real and imaginary values of c, corresponding
// to that x,y pixel in the image
double cr = mapToReal(x, imageWidth, minR, maxR);
double ci = mapToImaginary(y, imageHeight, minI, maxI);
//... Find the number of iterations in the Mandelbrot formula
// using said c.
int n = findMandelbrot(cr, ci, maxN);
//... Map the resulting number to an RGB value.
int r = (n % 256);
int g = (n % 256);
int b = (n % 256);
//... Output it to the image
f_out << r << " " << g << " " << b << " ";
}
f_out << std::endl;
}
}
int main()
{
//Initializes file
f_out << "P3" << std::endl;
f_out << imageWidth << " " << imageHeight << std::endl;
f_out << "256" << std::endl;
//For every pixel...
threadedMandelbrot();
f_out.close();
std::cout << "Helemaal klaar!" << std::endl;
return 0;
}
Your are calculating only a quarter of the image, so you have to set the dimension of that to halfHeight, halfWidth or fill the file with zeroes. When the image viewer reads the file, it shows two lines of it in a single line of pixels untill it reaches the end of the file, at a quarter of the picture height.
To fix the problem you just have to calculate the other three quarters of the image, but I suggest you to seperate the calc function from the file writing function: do the threaded calcs putting the result in an array (std::array or std::vector), look up the right color and then write to file.
Been stuck on this a few days now, I'm going out my mind. Essentially I have converted a 2D array of values (an image, I know there are easier ways to achieve this but I have explicit requirements) into a 1D array. I can rotate the 2D array with ease. I'm having trouble with the rotating of the 1D version of the array, and I think it's down to a single line of algorithm being incorrect.
The code I'm using for rotating the array is:
cout << "\nTransfer from 2D to dynamic 1D array and print details\n\n";
myImage * p_myImage = new myImage[total];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int offset = width * y + x;
p_myImage[offset].pixel = array2D[y][x];
cout << p_myImage[offset].pixel << " ";
}
cout << "\n";
}
//new pointer to copy to
myImage * p_myImage2 = new myImage[total];
cout << "\nRotate Matrix through 1D array\n\n";
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
int offset = height * x + y;
//int offset = width * y + x ;
p_myImage2[offset].pixel = p_myImage[height-1+x].pixel;
cout << p_myImage2[offset].pixel << " ";
}
cout << "\n";
}
This should rotate it clockwise:
p_myImage2[offset].pixel = p_myImage[width * (height - 1 - y) + x].pixel;