going from Inverse discrete FFT to spectrogram - c++

I need to locate some generic C++ library that takes the inverse fft output (fftw_complex format, i.e. two doubles) and converts this data to an image file such as png. I can waterfall the dffts to obtain the 2d data (and use 10log10(rere+imim) to obtain magnitudes for each frequency component) but I don't know which image library will work.
I did use an older program called zimage at one time, but it seems no longer available. I do not have MATLAB on my Ubuntu 9.10 system (but I do have Octave)
Can Octave generate the waterfall images? I also need to convert the spectrogram into a wav sound file too.
Any ideas??

The easiest image format to create is PNM. You can print it as a text file, and then convert it using most graphics programs. Here is an example from the Wikipedia page:
P2 24 7 15
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 3 3 3 3 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 15 15 15 0
0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 15 0
0 3 3 3 0 0 0 7 7 7 0 0 0 11 11 11 0 0 0 15 15 15 15 0
0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 0 0
0 3 0 0 0 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Save that text in a file named "feep.pgm", and you'll see what I mean.
http://en.wikipedia.org/wiki/Netpbm_format
You'll have to scale your 10log10 information to pixel values.

OpenCV is one library that can handle PNG files, among a variety of other formats. It should be readily available on your Ubuntu 9.10 system using apt get libcv-dev (from memory, you may have to double-check the package name).
/*
* compile with:
*
* g++ -Wall -ggdb -I. -I/usr/include/opencv -L /usr/lib -lm -lcv -lhighgui -lcvaux filename.cpp -o filename.out
*/
#include <cv.h>
#include <highgui.h>
/*
* Your image dimensions.
*/
int width;
int height;
CvSize size = cvSize(width, height);
/*
* Create 3-channel image, unsigned 8-bit per channel.
*/
IplImage *image = cvCreateImage(size, IPL_DEPTH_8U, 3);
for (int i = 0; i < width; ++i)
for (int j = 0; j < height; ++j)
{
unsigned int r;
unsigned int g;
unsigned int b;
/*
* Keep in mind that OpenCV stores things in BGR order.
*/
CvScalar bgr = cvScalar(b, g, r);
cvSet2D(image, i, j, bgr);
}
cvSaveImage("filename.png", image);
cvReleaseImage(&image);

Related

OpenCV's connectedComponentsWithStats() returns NaN position for first centroid

I am using the OpenCV C++ API (version 4.7.0). I have an application where I need to determine the centroid of a laser spot through a camera feed, and to do that I am attempting to use the connectedComponentsWithStats() function.
In my current setup, the image data is defined in a text file as a 2D array. This data is of a binary image, which is expected by connectedCoponentsWithStats(). The size of the image is 625X889. Here is what the binary image looks like:
This is the result of a binary thresholding operation.
Here is my C++ code for computing the centroids of the connected components:
/* Grouping and centroiding implementation using OpenCV's
* ConnectedComponentsWithStats().
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>
using namespace std;
int main()
{
uint32_t ROWS=625;
uint32_t COLS=889;
uint8_t image_array[ROWS][COLS];
uint8_t *image_data = new uint8_t[ROWS * COLS];
int i;
int j;
//Read data from file and save into image_array[][].
ifstream fp("./data/binary_image/binary_image1.txt");
if (! fp)
{
cout << "Error opening file" << endl;
return 1;
}
for (i=0; i<ROWS; i++)
{
for (j=0; j<COLS; j++)
{
fp >> image_array[i][j];
if (! fp)
{
cout << "Error reading file for element " << i << "," << j << endl;
return 1;
}
}
}
//Copy data into a 1D array called image_data.
for (i=0; i<ROWS; i++)
{
for (j=0; j<COLS; j++)
{
if (image_array[i][j] == 1)
{
image_array[i][j] = 255;
}
image_data[i * COLS + j] = image_array[i][j];
}
}
//Create image object.
cv::Mat image(ROWS, COLS, CV_8U, image_data);
//Create output objects.
cv::Mat labels, stats, centroids;
//Pass image to connectedComponentsWithStats() function.
int num_components = cv::connectedComponentsWithStats(image, labels, stats, centroids);
//Print out the centroids
for (i=0; i<num_components; i++)
{
cout << "Centroid "<< i << ": (" << centroids.at<double>(i, 0) << ", " << centroids.at<double>(i,1) << ")" << endl;
}
return 0;
}
Not that is it important, but this is how I compiled the code:
g++ `pkg-config --cflags opencv4` -c opencv_grouping.cpp
g++ opencv_grouping.o `pkg-config --libs opencv4` -o opencv_test
This is the output after running ./opencv_test:
Centroid 0: (nan, nan)
Centroid 1: (444, 312)
I am not really sure why I am getting the nan result, but I suppose this means that the area of that connected component is 0? As for the (444, 312) result, that just corresponds (roughly) to the center of a 625X889 grid, which is the size of the inputted image. So it essentially just computed the center of that grid. I tried with several other input images of the same size that were generated using different thresholds (so the spot size varried), and it yielded exactly the same result.
Does anyone have any insight/suggestions as to what might be going on here? Does OpenCV provide other functions that can be used to compute the centroid of the spot?
--Update--
I think that I found out where the problem is coming from. Something very strange is happening when I am reading in the data from the text file. It's not reading the data correctly at all. It gets to row 440 and then the rest of the data is either 0 or some garbage value (like a really large negative number for example). I am not sure why this is happening. In addition to declaring image_array as uint8_t image_array[ROWS][COLS], I have also tried allocating memory for it with new:
uint8_t **image_array = new uint8_t*[ROWS];
for (i=0; i<ROWS; i++)
{
image_array[i] = new uint8_t[COLS];
}
Both of these run into the same problem. As a sanity check, I created a smaller (19X20) array to test with. It looks like this in a text file:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 255 255 255 0 0 0 0 0 0 255 255 255 255 255 0 0 0
0 0 0 255 255 255 0 0 0 0 0 0 0 255 255 255 0 0 0 0
0 0 255 255 255 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 255 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0
0 0 0 255 255 255 255 255 0 0 0 0 255 255 255 255 0 0 0 0
0 0 0 0 255 255 255 255 255 0 0 255 255 255 0 0 0 0 0 0
0 0 0 0 0 0 255 255 255 255 255 255 255 0 0 0 0 0 0 0
0 0 0 0 0 0 0 255 255 255 255 255 255 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 255 255 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
I run this through the code my code (with ROWS=19 and COLS=20) and it actually gives a reasonable output for the centroids:
Centroid 0: (9.63497, 9.22086)
Centroid 1: (3.8, 3.1)
Centroid 2: (14, 2.55556)
Centroid 3: (8.71429, 10.2857)
So the issue is that the larger array is not getting read from the text file correctly. Any suggestions?

What is the 5x5 equivalent of the 3x3 emboss kernel?

-2 -1 0
-1 1 1
0 1 2
This is 3x3 emboss kernel. How should I write this in 5x5?
As I understand, these filters take directional differences (see the wikipidea page).
We can decompose you filter into directions
0 -1 0 0 0 0 -2 0 0
0 0 0 -1 0 1 0 0 0
0 1 0 0 0 0 0 0 2
So, I think you can expand it over these 3 directions giving emphasis
0 0 -1 0 0 0 0 0 0 0 -2 0 0 0 0
0 0 -1 0 0 0 0 0 0 0 0 -2 0 0 0
0 0 0 0 0 -1 -1 0 1 1 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 2 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 2
So, the final kernel would be
-2 0 -1 0 0
0 -2 -1 0 0
-1 -1 1 1 1
0 0 1 2 0
0 0 1 0 2
May be you can also try interpolating filter coefficients marked as x
-2 x -1 0 0
x -2 -1 0 0
-1 -1 1 1 1
0 0 1 2 x
0 0 1 x 2
The simple solution to fitting any lower-dimensional convolution kernel into a higher-dimensional matrix of the same rank is to surround it by zero weights. This is especially true when you're dealing with a concept like embossing, which is arguably more interested in immediate vector of change than the rate at which it is changing. That is, for this embossing matrix,
You could equivalently use this in 5 x 5:
Granted, this will get you a different visual effect than anything with any part of the matrix filled in; but sometimes, especially with edge-detection, immediate clarity is more important. We aren't always displaying it. If this were something like a Guassian blur kernel, having a greater range could improve the effect, but embossing isn't that different conceptually from Sobel-Feldman and it may be better to keep it tight.

Eigen: Zero matrix with smaller matrix as the "diagonals"

Is it possible to create a 9x9 matrix where the "diagonal" is another matrix and the rest are zeroes, like this:
5 5 5 0 0 0 0 0 0
5 5 5 0 0 0 0 0 0
5 5 5 0 0 0 0 0 0
0 0 0 5 5 5 0 0 0
0 0 0 5 5 5 0 0 0
0 0 0 5 5 5 0 0 0
0 0 0 0 0 0 5 5 5
0 0 0 0 0 0 5 5 5
0 0 0 0 0 0 5 5 5
from a smaller 3x3 matrix repeated:
5 5 5
5 5 5
5 5 5
I am aware of the Replicate function but that repeats it everywhere in the matrix and doesn't maintain the zeroes. Is there a builtin way of achieving what I'm after?
One way of doing this is by using blocks where .block<3,3>(0,0) is a 3x3 block starting at 0,0. (Note: Your IDE might flag this line as an error but it will compile and run)
for (int x=0, x<3, x++){
zero_matrix.block<3,3>(x*3,x*3) = five_matrix;
}
You can use the (unsupported) KroneckerProduct module for that:
#include <unsupported/Eigen/KroneckerProduct>
int main()
{
Eigen::MatrixXd A = Eigen::kroneckerProduct(Eigen::Matrix3d::Identity(), Eigen::Matrix3d::Constant(5));
std::cout << A << '\n';
}

Random function keeps on getting same result [duplicate]

This question already has answers here:
srand() — why call it only once?
(7 answers)
Closed 7 years ago.
I am writing a program to simulate Knight's tour randomly. (See wikipedia for what it means: http://en.wikipedia.org/wiki/Knight%27s_tour) First, I create a chess object, which is basically just a 8*8 array with numbers to indicate the position of the knight. I create a chess object and randomly assign a position for the knight. Then, I moved the knight randomly until there is no more legal moves and returns the number of moves performed.
int runTour ()
{
srand (time(NULL));
Chess knight(rand()%8, rand()%8); //Initialize random chess object.
knight.printBoard(); //Prints the board before moving
int moveNumber = 0; //A number from 0 to 7 that dictates how the knight moves
int counter = 0;
while (moveNumber != -1) //A moveNumber of -1 means there is no more legal move
{
moveNumber = knight.findRandMove(knight.getRow(), knight.getColumn()); //findRandMove is a function that returns a legal random move for the knight based on its position. It works perfectly.
knight.move(moveNumber); //move is a function that moves the knight
counter ++;
}
knight.printBoard(); // Returns board when move is exhausted
return counter; //Returns number of moves performed.
}
The interesting thing is that while it runs perfectly randomly from run to run, it keeps outputting the same thing in the same run. For example, this is the main() function:
int main(){
runTour();
runTour();
return 0;
}
And in BOTH runTour() it outputs: (where 0 represents positions not reached, 1 represents the current position of the knight, and 9 positions reached)
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 9 0 0 0
0 9 9 0 0 0 9 0
0 0 0 0 0 9 9 0
9 0 9 9 9 9 0 1
0 0 9 9 9 9 9 9
0 9 9 9 9 0 9 0
9 0 0 0 9 9 9 9
0 0 9 0 9 9 0 9
And when I run it again, BOTH runTour output:
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 9 9
0 9 0 0 9 9 9 0
0 0 9 9 9 9 9 9
1 0 9 0 9 9 0 9
So the random function is random in different runs, but is the same in each run. Why is this the case? How can I modify the code so that runTour() can have different performances when it is called? Thank you very much for reading this clumsy question.
As you´re using a timestamp as srand seed:
If both runTours are in the same second, what do you think will happen with your code?
...
srand is supposed to be called exactly one time, not one time per function call of runTour
Try moving your srand call to your main function. You should only have to seed the generator one time, rather than each time you call the function.

Matlab: how to work with sparse keys to access sparse data?

I am trying to access the sparse mlf with the keys such as BEpos and BEneg where one key per line. Now the problem is that most commands are not meant to deal with too large input: bin2dec requires clean binary numbers without spaces but the regexp hack fails to too many rows -- and so on.
How to work with sparse keys to access sparse data?
Example
K>> mlf=sparse([],[],[],2^31,1);
BEpos=Cg(pos,:)
BEpos =
(1,1) 1
(2,3) 1
(2,4) 1
K>> mlf(bin2dec(num2str(BEpos)))=1
Error using bin2dec (line 36)
Binary string must be 52 bits or less.
K>> num2str(BEpos)
ans =
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
K>> bin2dec(num2str('1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0'))
Error using bin2dec (line 36)
Binary string must be 52 bits or less.
K>> regexprep(num2str(BEpos),'[^\w'']','')
Error using regexprep
The 'STRING' input must be a one-dimensional array
of char or cell arrays of strings.
Manually works
K>> mlf(bin2dec('1000000000000000000000000000000'))
ans =
All zero sparse: 1-by-1
Consider a different approach using manual binary to decimal conversions:
pows = pow2(size(BEpos,2)-1 : -1 : 0);
inds = uint32(BEpos*pows.')
I haven't benchmarked this, but it might work faster than bin2dec and cell arrays.
How it works
This is pretty simple: the powers of 2 are calculated and stored in pows (assuming the MSB is in the leftmost position). Then they are multiplied by the bits in the matching positions and summed to produce the corresponding decimal values.
Try to index with this:
inds = uint32( bin2dec(cellstr(num2str(BEpos,'%d'))) );