1

I've an image loaded in a Mat. From this, I calculate the DFT, so in a new Mat I've the DFT stored (both the real and img. parts).

Since here, I aim to draw the magnitude of the DFT. I've achieved this already with OpenCV, by calculating it, take it to the range 0 - 1 and show it with imshow.

However, now I'm working with winform, so I need to convert my image to bitmap in order to draw it in a PictureBox.

I've also achieved to draw some kinds of images in it, using for example:

private: void DrawCVImageGrayScale(System::Windows::Forms::Control^ control, cv::Mat& colorImage) {

            System::Drawing::Graphics^ graphics = control->CreateGraphics();
            System::IntPtr ptr(colorImage.ptr());
            System::Drawing::Bitmap^ b  = gcnew System::Drawing::Bitmap(colorImage.cols,colorImage.rows,colorImage.step,System::Drawing::Imaging::PixelFormat::Format8bppIndexed,ptr);
            System::Drawing::RectangleF rect(0,0,control->Width,control->Height);
            graphics->DrawImage(b,rect);

}

I was able to draw a CV_8UC1 into the control (PictureBox). But I've no achieved to draw my CV_32FC1 into it.

I had tried with several possibilities, but I think the problem is this one: Since my image is CV_32FC1, and bitmap has no pixelFormat with this configuration, I would need to convert it first.

What have I exactly done?

I've scaled my CV_32FC1 Mat, to ensure values in the 16b range (magI is my magnitude Mat):

// Ahora lo llevamos de 0 a 255
double OldMax, OldMin;
double NewMax = 65535, NewMin=0;
minMaxLoc(magI, &OldMin, &OldMax);

double OldRange = (OldMax - OldMin);
double NewRange = (NewMax - NewMin);
magI = (((magI - OldMin) * NewRange) / OldRange) + NewMin;

Mat_<unsigned short int> resultado(magI.size());

for(int i = 0; i < magI.rows; i++)
{
    for(int j = 0; j < magI.cols; j++)
    {
        float grayPixel = magI.at<float>(i, j);

        resultado.at<unsigned short int>(i, j) = grayPixel;

    }
}

I've checked that unsigned short int has 2 bytes as size, in order to match with 16b from Format16bppGrayScale pixelFormat. But when I try this combination (yes, If I choose another pixelFormat, it paints a nonsense, but it paints) I get a:

A generic error occurred in GDI+

To finalize, I post here my result with Format16bppRgb555.

DFT Magnitude

The problem is, that my magnitude is in a CV_16UC1 (since I created a Mat_<unsigned short int>), so to choose Format16bppRgb555 makes no sense, even if it appears to be near to the good result.

Any idea about it would be appreciated!

Thanks you in advance

EDIT: I've been thiking that with Format16bppRgb555 it's painting only the first channel (the only one I've, B-GR) so the result is exactly what I want, but in blue channel...

EDIT 2 (EDIT 5: you can jump from here to EDIT 5): I've tried what @BoykoPerfanov has told me, which translated to C++ code is:

cv::Mat adaptada(Image.size(), CV_16UC3);

uint16_t* pixelPtr = (uint16_t*) Image.data;
uint16_t* pixelPtrDest = (uint16_t*) adaptada.data;

for(int i = 0; i < Image.rows; i++)
{

    for(int j = 0; j < Image.cols; j++)
    {

        float src_pixelval = pixelPtr[Image.channels()*(Image.cols*i + j)];

        uint16_t conv = cvRound(src_pixelval * 32); //converted value to integral 5-bit type

        pixelPtrDest[adaptada.channels()*(adaptada.cols*i + j)] = (conv<<0 | (conv<<5) | (conv<<10));

    }

}

Now, when I paint it, I get:

enter image description here

So I guess I've not understood it well, or I'm missing something at coding time..

EDIT 3: Since my Mat is CV_32F, I've noticed my declaration:

uint16_t * pixelPtr = (uint16_t *) colorImage.data;

Was wrong, and changed it to uint32_t, same as src_pixelval type. Now my result is:

enter image description here

EDIT 4 (oh my god): I realized that I had not changed the painting step from the bitmap to the new Mat, so changed that line to:

System::Drawing::Bitmap^ b  = gcnew System::Drawing::Bitmap(adaptada.cols,adaptada.rows,adaptada.step,System::Drawing::Imaging::PixelFormat::Format16bppRgb555 ,ptr);

Now I get (resized a bit this time):

enter image description here

EDIT 5: I've coded again the function to get the values, painting it as a Format48bppRgb, where each 16b are my 16b original values from my CV_32FC1 Mat. I guess my problem now is just that overflow, and I must normalize that values, cause I got the same problem when painting it in OpenCV (that the magnitude seems inverted black/white). Let's see code and results:

cv::Mat adaptada(colorImage.size(), CV_16UC3);

uint32_t * pixelPtr = (uint32_t *) colorImage.data;
uint16_t* pixelPtrDest = (uint16_t*) adaptada.data;

for(int i = 0; i < colorImage.rows; i++)
{

    for(int j = 0; j < colorImage.cols; j++)
    {

        uint32_t src_pixelval = pixelPtr[colorImage.channels()*(colorImage.cols*i + j)];

        uint16_t conv = cvRound(src_pixelval * 32);

        pixelPtrDest[adaptada.channels()*(adaptada.cols*i + j) + 0] = conv;
        pixelPtrDest[adaptada.channels()*(adaptada.cols*i + j) + 1] = conv;
        pixelPtrDest[adaptada.channels()*(adaptada.cols*i + j) + 2] = conv;

    }

}

System::IntPtr ptr(adaptada.ptr());
System::Drawing::Bitmap^ b  = gcnew System::Drawing::Bitmap(adaptada.cols,adaptada.rows,adaptada.step,System::Drawing::Imaging::PixelFormat::Format48bppRgb ,ptr);

enter image description here

Community
  • 1
  • 1
Btc Sources
  • 1,912
  • 2
  • 30
  • 58

2 Answers2

1

Format16bppRgb555 has memory layout of 1 bit (unused), 5 bits red (0-32), 5 bits green(0-32), 5 bits blue(0-32). You need to pack the bits manually, because opencv does not support creating a gray image from a grayscale image, except when the input type is CV_8UC1.

A very basic solution is as follows:

    foreachpixel
{
  float src_pixelval;
  uint16_t* dst = ...;
  uint16_t conv = round(src_pixelval * 32); //converted value to integral 5-bit type

Now write in the red, green, and blue output channels (to produce grayscale bitmap) by:

 dst = (conv<<0 | (conv<<5) | (conv<<10));
}

What does it do? In the memory, your pixel data is saved in a floating-point layout (explained here), having sign, exponent, and mantissa

float: (-1^sign (bits a to b) * mantissa(bits c to d) * 2^exponent(bits e to f))

When we converted the value to a uint16_t (important: platform-unspecific, guaranteed to be 16 bit number), we converted the number in the standard binary integral layout:

2^16 * (senior_bit) + 2^15 * (bit1) + 2^14 * (bit2) + ... 1 * (junior_bit)

But we actually treat the number as a 5-bit number (defined by rgb555 standard). Assuming that your float values go from 0 to 1, you want to scale them to span the range of a 5-bit integer (0 to 32).

Finally, we copy this 5-bit number to the r, g, and b channel of the destination pixel, forming the desired layout. For example, pixel brightness = 0.78 -> converted brightness = 0.78*32 = 25, which is 11001. If you're up to doing DFT chances are you should know (or find your way easily around) bitwise operations like bitshift and bitwise or.

u(unused/undefined) | red 11001 | blue 11001 | green 11001 -> u110011100111001.

Boyko Perfanov
  • 3,007
  • 18
  • 34
0

I've managed to solve it thanks to the answer of Boyko, but a little changed, so I'm gonna explain what have I done to do it.

As Boyko said:

Format16bppRgb555 has memory layout of 1 bit (unused), 5 bits red (0-32), 5 bits green(0-32), 5 bits blue(0-32). You need to pack the bits manually, because opencv does not support creating a gray image from a grayscale image, except when the input type is CV_8UC1.

Then, his approach to how to take the values of each pixel gave me wrong values, because I was taking values from a CV_32FC1, so when I go to a pixel using a uint32_t *, the returned value was int instead of float. This made the program to do it wrong (even if I obtain these values from uint32_t and then put it into a float var).

So I changed the pointer to my original Mat, from uint32_t to float (knowing that my float has the same size, 4 bytes), so I could get the correct value, and stored it in a float var too.

foreachpixel
{
  float src_pixelval;
  float * dst = ...;    //Get the pointer to my pixel, from my Mat

Then I had to pass it to my new Mat. Since I had high values, I eventually changed it in order to don't loose data. So I changed to Format48bppRgb in order to don't need to scale it that much (I didn't have my float values between 0 and 1, and I didn't want to round them that much).

So I got 16b for each value, changing my dst Mat to a CV_16UC3 in order to fit with 48b from Format48bppRgb. Therefore, my value was obtained and transferred to my new mat like:

  uint16_t conv = cvRound(src_pixelval * 512);

  pixelPtrDest[adaptada.channels()*(adaptada.cols*i + j) + 0] = conv;
  pixelPtrDest[adaptada.channels()*(adaptada.cols*i + j) + 1] = conv;
  pixelPtrDest[adaptada.channels()*(adaptada.cols*i + j) + 2] = conv;
}

The reason to multiply it with 512 is that I had my values in CV_32FC1 between 0 and 255, so I take them to the 16b range doing it.

I also changed the access way to the new Mat, but this is just a code way to do it, Boyko way worked fine too.

Then, I finally get:

enter image description here

Community
  • 1
  • 1
Btc Sources
  • 1,912
  • 2
  • 30
  • 58