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) << " ";
}
}
}
Related
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 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.
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 have looped through the Main image by blocks of width: 49 and height: 36 and I want to 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.
This is the code for loop of the Main image:
for (double bx = 0; bx < M; bx += R)
for (double by = 0; by < N; by += C)
{
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) << " ";
}
cout << MainIMG_2DVector[bx + x][by + y] << " ";
}
}
cout << "\n\n\n" << endl;
}
This loop displays all the blocks in one go. The problem I'm having is that I don't know how to put each block into an array, so I can compare the data.
Also is it better to use a pointer instead of an array to do this?
Thanks
I'm not sure exactly how you want to compare the blocks, but as far as storing the blocks, you can make a simple Block object like this:
const double R = 49, C = 36;
// Block object (takes in your image 2D vector and x/y coordinates)
class Block {
public:
Block(std::vector<std::vector<int>> *img_vector, int bx, int by);
int compare(const Block &block) const;
private:
std::vector<std::vector<int>> *img_vector;
int bx, by;
};
Block::Block(std::vector<std::vector<int>> *img_vector, int bx, int by) {
this->img_vector = img_vector;
this->bx = bx;
this->by = by;
}
// Compare any two blocks
int Block::compare(const Block &block) const {
for (int x = bx; x < bx + R; x++) {
for (int y = by; y < by + C; y++) {
// Do some comparing
std::cout << "Compare " << (*img_vector)[x][y]
<< " with " << (*block.img_vector)[x][y] << std::endl;
}
}
return 0; // Return some value that indicates how they compare
}
And then add the image blocks to a vector:
// Add main image blocks to vector
std::vector<Block> main_img_blocks;
for (double bx = 0; bx < M; bx += R) {
for (double by = 0; by < N; by += C)
main_img_blocks.push_back(Block(&MainIMG_2DVector, bx, by));
}
// Do the same for sub image blocks...
// Invoke the compare function between 2 blocks at a time
int comp_value = main_img_blocks[0].compare(main_img_blocks[1]);
Hope that helps :)
Iterating through 1D array (pseudo 2D) with step of 3:
arr = new int[height * width * 3];
for (int i = 0; i < height * width * 3; i+=3) {
arr[i] = 1;
}
I have tried this, but what I got is column of one third:
for (int y = 0; y < height * 3; y++) {
for (int x = 0; x < width; x+=3) {
arr[x + width * y] = 1;
}
}
Assuming your cells have a 'size' of 3 entries, you should use the * 3 on the inner loop. Otherwise you miss 2 thirds of your cells on each row.
You also need to multiply width by 3 to get the correct row.
for (int y = 0; y < height; y++) {
for (int x = 0; x < width * 3; x+=3) {
arr[x + width * 3 * y] = 1;
}
}
In general you need the following structure for such situations:
for (int y = 0; y < height; y++) {
for (int x = 0; x < width * cellWidth; x+= cellWidth) {
arr[x + width * cellWidth * y] = 1;
}
}
(Were cellWidth is 3 in your case)
To slightly simplify this, you could assume in the loops that your cells have a width of 1 (like a normal situation) and multiply by cellWidth when actually assigning the values:
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int index = (x + width * y) * cellWidth;
arr[index + 0] = 1; // First 'cell entry'
arr[index + 1] = 1; // Second
...
arr[index + cellWidth - 1] = 1; // Last
}
}
Another solution is to create larger 'items' using a struct for example:
typedef struct { int r, int g, int b } t_rgb;
t_rgb* arr = new t_rgb[height * width];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
arr[x + width * y].r = 1;
}
}
and you are able to use it as a regular array (the compiler does all calculations for you). This also makes it more clear what is happening in your code.
What are you trying to accomplish exactly? Setting a channel in a RGB image?
I usually do it like this:
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
arr[(x + width * y) * 3] = 1;
In general, to set RGB values, you can simply add an offset like this:
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
size_t base = (x + width * y) * 3;
arr[base + 0] = r;
arr[base + 1] = g;
arr[base + 2] = b;
}
I am building a game of life CA in C++ (openFrameworks). As I am new to C++ I was wondering if someone could let me know if I am setting up the vectors correctly in the following code. the CA does not draw to the screen and I am not sure if this is as a result of how I set up the vectors. I have to use 1D vectors as I intend to send data to Pure Data which only handles 1D structures.
GOL::GOL() {
init();
}
void GOL::init() {
for (int i =1;i < cols-1;i++) {
for (int j =1;j < rows-1;j++) {
board.push_back(rows * cols);
board[i * cols + j] = ofRandom(2);
}
}
}
void GOL::generate() {
vector<int> next(rows * cols);
// Loop through every spot in our 2D array and check spots neighbors
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
// Add up all the states in a 3x3 surrounding grid
int neighbors = 0;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
neighbors += board[((x+i+cols)%cols) * cols + ((y+j+rows)%rows)];
}
}
// A little trick to subtract the current cell's state since
// we added it in the above loop
neighbors -= board[x * cols + y];
// Rules of Life
if ((board[x * cols + y] == 1) && (neighbors < 2)) next[x * cols + y] = 0; // Loneliness
else if ((board[x * cols + y] == 1) && (neighbors > 3)) next[x * cols + y] = 0; // Overpopulation
else if ((board[x * cols + y] == 0) && (neighbors == 3)) next[x * cols + y] = 1; // Reproduction
else next[x * cols + y] = board[x * cols + y]; // Stasis
}
}
// Next is now our board
board = next;
}
this looks weird in your code:
void GOL::init() {
for (int i =1;i < cols-1;i++) {
for (int j =1;j < rows-1;j++) {
board.push_back(rows * cols);
board[i * cols + j] = ofRandom(2);
}
}
}
"vector.push_back( value )" means "append value to the end of this vector" see std::vector::push_back reference
After doing this, you access the value of board[i * cols + j] and change it into a random value. What I think you are trying to do is:
void GOL::init() {
// create the vector with cols * rows spaces:
for(int i = 0; i < cols * rows; i++){
board.push_back( ofRandom(2));
}
}
This is how you would access every element at position x,y in your vector:
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
board[x * cols + y] = blabla;
}
}
This means that in void GOL::generate() you are not accessing the right position when you do this:
neighbors += board[((x+i+cols)%cols) * cols + ((y+j+rows)%rows)];
I think you want to do this:
neighbors += board[((x+i+cols)%cols) * rows + ((y+j+rows)%rows)];
so x * rows + y instead of x * cols + y