QImage (or images generally) conversion to 3 1D arrays for RGB - c++

A function that I am trying to conform to requires three 1-Dimensional arrays of type double[19200]. The following arrays are RGB arrays such that:
double r[19200]; // r
double g[19200]; // g
double b[19200]; // b
So far, I can extract pixel information from a QImage and populate the above arrays.
The problem is with testing. I don't know how to do the inverse: given the three 1-Dimensional arrays how do I create a new QImage from this data?
I would like to verify that I am indeed getting the correct values. (Things like column vs. row major order is giving me doubts). As a result, I am trying to construct an image a QImage from these three 1-D Dimensional arrays.

I don't really understand why you're having a problem if you managed to do it one way. The process is essentially the same:
for (int x=0; x<w; x++)
for (int y=0; y<h; y++)
image.setPixel(x,y, convertToRGB(r[x*w+y], ...);
Where convertToRGB is the inverse transform of what you to to convert and RGB value to your float values, supposing the image has dimension w*h. If you discover this is the wrong row-major/column major variant, just inverse it.
Since you gave no info about how you do the color space conversion, and we don't know if it's row-major or column-major either, can't help you much more than that.

Well it looks like QImage supports a couple of ways to load from pixel arrays.
QImage(const uchar *data, int width, int height, Format format)
bool QImage::loadFromData(const uchar *buf, int len, const char *format=0)
Using the first example, if you have the arrays you mention, then you will likely want to use the format QImage::Format_RGB888 (from qimage.h).
You will need to know the width and height yourself.
Finally you will want to repack your arrays into a single uchar* array
uchar* rgb_array = new uchar[19200+19200+19200];
for( int i = 0, j = 0; j < 19200; ++j )
{
// here we convert from the double range 0..1 to the integer range 0..255
rgb_array[i++] = r[j] * 255;
rgb_array[i++] = g[j] * 255;
rgb_array[i++] = b[j] * 255;
}
{
QImage my_image( rgb_array, width, height, QImage::Format_RGB888 );
// do stuff with my_image...
}
delete[] rgb_array; // note you need to hold onto this array while the image still exists

Related

Asigning pixel intensities to an unitialized Mat matrix using pointer

I try to extract the region of interest (ROI) in a Matrix in OpenCV. It can be easy to do by cv:Rect, e.g., im_roi = im(Rect(x,y, width, height)). But I prefer to get the data directly from the memory using pointers, which is presumably more efficient. Here below are my codes:
Mat im_roi; //the desired matrix holding ROI of im, uninitialized
uchar* im_roi_data = im_roi.data;
uchar* im_data = im.data;
int xstart = x;
int xend = xstart + width;
int ystart = y;
int yend = ystart + height;
for(ii=ystart; ii<yend; ii++)
{
for(jj=xstart; jj<xend; jj++) //the typo 'jj<xstart' was corrected
{
*im_roi_data++ = *im_data++;
*im_roi_data++ = *im_data++;
*im_roi_data++ = *im_data++;
}
im_data +=3*(im.cols-width);
}
The above for-loop codes however do not proceed. I feel the problem may be due to the uninitialized im_roi.
I think your second for loop needs to be:
for(jj=xstart; jj<xend; jj++)
As Mark Setchell noted it is not the only problem with your code, but yes you must initialize im_roi before accassing its pixels.
Using memcpy to copy content of whole row will be much more effecient then copying data pixel by pixel.
Writing im(Rect(x,y, width, height)).copyTo(im_roi); will be the cleanest AND fastest method of coping ROI (and in that case you don't need to initialize im_roi).

How to access matrix data in opencv by another mat with locations (indexing)

Suppose I have a Mat of indices (locations) called B, We can say that this Mat has dimensions of 1 x 100 and We suppose to have another Mat, called A, full of data of the same dimensions of B.
Now, I would access to the data of A with B. Usually I would create a for loop and I would take for each elements of B, the right elements of A. For the most fussy of the site, this is the code that I would write:
for(int i=0; i < B.cols; i++){
int index = B.at<int>(0, i);
std::cout<<A.at<int>(0, index)<<std:endl;
}
Ok, now that I showed you what I could do, I ask you if there is a way to access the matrix A, always using the B indices, in a more intelligent and fast way. As someone could do in python thanks to the numpy.take() function.
This operation is called remapping. In OpenCV, you can use function cv::remap for this purpose.
Below I present the very basic example of how remap algorithm works; please note that I don't handle border conditions in this example, but cv::remap does - it allows you to use mirroring, clamping, etc. to specify what happens if the indices exceed the dimensions of the image. I also don't show how interpolation is done; check the cv::remap documentation that I've linked to above.
If you are going to use remapping you will probably have to convert indices to floating point; you will also have to introduce another array of indices that should be trivial (all equal to 0) if your image is one-dimensional. If this starts to represent a problem because of performance, I'd suggest you implement the 1-D remap equivalent yourself. But benchmark first before optimizing, of course.
For all the details, check the documentation, which covers everything you need to know to use te algorithm.
cv::Mat<float> remap_example(cv::Mat<float> image,
cv::Mat<float> positions_x,
cv::Mat<float> positions_y)
{
// sizes of positions arrays must be the same
int size_x = positions_x.cols;
int size_y = positions_x.rows;
auto out = cv::Mat<float>(size_y, size_x);
for(int y = 0; y < size_y; ++y)
for(int x = 0; x < size_x; ++x)
{
float ps_x = positions_x(x, y);
float ps_y = positions_y(x, y);
// use interpolation to determine intensity at image(ps_x, ps_y),
// at this point also handle border conditions
// float interpolated = bilinear_interpolation(image, ps_x, ps_y);
out(x, y) = interpolated;
}
return out;
}
One fast way is to use pointer for both A (data) and B (indexes).
const int* pA = A.ptr<int>(0);
const int* pIndexB = B.ptr<int>(0);
int sum = 0;
for(int i = 0; i < Bi.cols; ++i)
{
sum += pA[*pIndexB++];
}
Note: Be carefull with pixel type, in this case (as you write in your code) is int!
Note2: Using cout for each point access put the optimization useless!
Note3: In this article Satya compare four methods for pixel access and fastest seems "foreach": https://www.learnopencv.com/parallel-pixel-access-in-opencv-using-foreach/

Write 2D array to PGM grayscale image

I'm creating various 2D arrays of sizes from 100x100 to 2000x2000 elements. The values within the arrays can be clamped down to 0 - 255 gray scale and then need to be written to a PGM image in order to visually represent the data.
For example, I'm declaring the arrays globally as:
element case1[100][100];
element is a structure of double pixelValue and a Boolean value (that won't be used when actually writing to the file but is necessary in the program).
In writing to the PGM image, I am having errors considering the FILE *fp in this area of the code when writing after the header:
int *p
for (int x = 0; x < dimension; x++)
{
for (int y = 0; y < dimension; y++)
{ //also doesn't work as: fp << (unsigned char)case1[x][y].pix;
int pix = case1[x][y].pixelValue;
*p = pix;
fp << (unsigned char)*p;
}
}
fclose(fp);
I'm unsure of how to work with the pointer in order to get the pixelValue from each location within the 2D array. I need to be able to iterate through each pixelValue to get the visual representation of the data.
Thank you for your help!
Used fputc() instead so that I could directly insert values instead of using pointers.

C++ - Convert uint8_t* image data to double** image data

I am working on a C++ function (inside my iOS app) where I have image data in the form uint8_t*.
I obtained the image data using the code using the CVPixelBufferGetBaseAddress() method of the iOS SDK:
uint8_t *bPixels = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);
I have another function (from a third part source) that does some of the image processing functions I would like to use on my image data, but the input for the image data for these functions is double**.
Does anyone have any idea how to go about converting this?
What other information can I provide?
The constructor prototype for the class that use double** look like:
Image(double **iPixels, unsigned int iWidth, unsigned int iHeight);
Your uint8_t *bPixels seems to hold image data as 1-dimensional continuous array of height*width lenght. So to access pixel in the x-th row and y-th column you have to write bPixels[x*width+y].
Image() seems to work on 2-dimensional arrays. To access pixel like above you would have to write iPixels[x][y].
So you need to copy your existing 1-dimensional array to a 2-dimensional:
double **mypixels = new double* [height];
for (int x=0; x<height; x++)
{
mypixels[x] = new double [width];
for (int y=0; y<width; y++)
mypixels[x][y] = bPixels[x*width+y]; // attention here, maybe normalization is necessary
// e.g. mypixels[x][y] = bPixels[x*width+y] / 255.0
}
Because your 1-dimensional array has pixel of type uint8_t and the 2-dimensional one pixel of type double, you must allocate new memory. Otherwise, if both would have same pixel type, the more elegant solution (a simple map) would be:
uint8_t **mypixels = new uint8_t* [height];
for (int x=0; x<height; x++)
mypixels[x] = bPixels+x*width;
Attention: beside the problem of eventually necessary normalization, there is also a problem with the indices-compatibility! My examples assume that the 1-dimensional array is stored row-by-row and that the functions working on 2-dimensional index with [x][y] (that means first-row-then-column). The declaration of Image() however, could lead to the conclusion that it needs its arrays to be indexed with [y][x] maybe.
I'm going to take a giant bunch of guesses here in hopes that this will lead you towards getting at the documentation and answering back. If there's no further documentation, well, here's a starting point.
Guess 1) The Image constructor requires a doubly dimensioned array where each component is an R,G,B,Alpha channel in that order. So iPixels[0] is the red data, iPixels[1] is the green data, etc.
Guess 2) Because it's not integer data, the values range from 0 to 1.
Guess 3) All of this must be pre-allocated.
Guess 4) Image data is row-major
Guess 5) Source data is BRGA
So with that in mind, starting with bPixels
double *redData = new double[width*height];
double *greenData = new double[width*height];
double *blueData = new double[width*height];
double *alphaData = new double[width*height];
double **iPixels = new double*[4];
iPixels[0] = redData;
iPixels[1] = greenData;
iPixels[2] = blueData;
iPixels[3] = alphaData;
for(int y = 0;y < height;y++)
{
for(int x = 0;x < width;x++)
{
int alpha = bPixels[(y*width + x)*4 + 3];
int red = bPixels[(y*width +x)*4 + 2];
int green = bPixels[(y*width + x)*4 + 1];
int blue = bPixels[(y*width + x)*4];
redData[y*width + x] = red/255.0;
greenData[y*width + x] = green/255.0;
blueData[y*width + x] = blue/255.0;
alphaData[y*width + x] = alpha/255.0;
}
}
Image newImage(iPixels,width,height);
some of the things that can go wrong.
Source is not BGRA but RGBA, which will make the colors all wrong.
Not row major or destination is not in slices which will make things look all screwed up and/or seg-fault

How to improve sorting pixels in cvMat?

I am trying to sort pixel values of an image (example 80x20) from lowest to highest.
Below is the some code:
bool sortPixel(int first, int second)
{
return (first < second);
}
vector<int>vect_sortPixel;
for(int y=0; y<height; y++)
{
for(int x=0; x<width; x++)
{
vect_sortPixel.push_back(cvGetReal2D(srcImg, y, x));
sort(vect_sortPixel.begin(), vect_sortPixel.end(), sortPixel);
}
}
But it takes quite long time to compute. Any suggestion to reduce the processing time?
Thank you.
Don't use getReal2D. It's quite slow.
Convert image to cv::Mat or Mat. Use its data pointer to get the pixel values. Mat.data() will give you pointer to the original matrix. Use that.
And as far as sorting is concerned, I would advise you to first make an array of all the pixels, then sort it using Merge sort (time complexity O(n log n))
#include<opencv2/highgui/highgui.hpp>
#include<stdio.h>
using namespace cv;
using namespace std;
int main()
{
Mat img = imread("filename.jpg",CV_LOAD_IMAGE_COLOR);
unsigned char *input = (unsigned char*)(img.data);
int i,j,r,g,b;
for(int i = 0;i < img.cols;i++){
for(int j = 0;j < img.rows;j++){
b = input[img.cols * j + i] ;
g = input[img.cols * j+ i + 1];
r = input[img.cols *j + i +2];
}
}
return 0;
}
Using this you can access pixel values from the main matrix.
Warning: This is not how you compare it. I'm suggesting that by using something like this, you can access pixel values.
Mat.data() gives you pointer to the original matrix. This matrix is a 1 D matrix with all the given pixel values.
Image => (x,y,z),(x1,y1,z1), etc..
Mat(original matrix) => x,y,z,x1,y1,z1,...
If you still have some doubts regarding how to extract data from Mat, visit this link OpenCV get pixel channel value from Mat image
and here's a link regarding Merge Sort http://www.cplusplus.happycodings.com/Algorithms/code17.html
There are few problems in your code:
As Froyo already said you use cvGetReal2D which is actually not very fast. You have to convert your cvMat to cv::Mat. To do this there's cv::Mat constructor:
// converts old-style CvMat to the new matrix; the data is not copied by default
Mat(const CvMat* m, bool copyData=false);
And after this use direct pixels acces as mentioned in this SO question.
Another problem is that you use push_back which actually also not very fast. You know the size of array, so why don't you allocate needed memory at the beginning? Like this:
vector<int> vect_sortPixel(mat.cols*mat.rows);
And than just use vect_sortPixel[i] to get needed pixel.
Why do you call sort in the loop? You have to call it after loop, when array is already created! Default STL's sort should work fast:
Complexity
Approximately N*logN comparisons on average (where N is
last-first). In the worst case, up to N^2, depending on specific
sorting algorithm used by library implementation.