I want to draw string or char (offscreen) and use it as Fl_Image or Fl_RGB_Image.
Based on this link I can do that easily with Fl__Image__Surface. The problem with Fl__Image__Surface is that it does not support transparency when I convert its output to image (Fl_RGB_Image) using image() method. So is there any way I can achieve this? I can do that on Java Swing with BufferedImage, also in Android with Canvas by creating Bitmap with Bitmap.Config.ARGB_8888.
If you prefer to do it manually, you can try the following:
#include <FL/Enumerations.H>
#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Device.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Image.H>
#include <FL/Fl_Image_Surface.H>
#include <FL/fl_draw.H>
#include <cassert>
#include <vector>
Fl_RGB_Image *get_image(int w, int h) {
// draw image on surface
auto img_surf = new Fl_Image_Surface(w, h);
Fl_Surface_Device::push_current(img_surf);
// We'll use white to mask 255, 255, 255, see the loop
fl_color(FL_WHITE);
fl_rectf(0, 0, w, h);
fl_color(FL_BLACK);
fl_font(FL_HELVETICA_BOLD, 20);
fl_draw("Hello", 100, 100);
auto image = img_surf->image();
delete img_surf;
Fl_Surface_Device::pop_current();
return image;
}
Fl_RGB_Image *get_transparent_image(const Fl_RGB_Image *image) {
assert(image);
// make image transparent
auto data = (const unsigned char*)(*image->data());
auto len = image->w() * image->h() * image->d(); // the depth is by default 3
std::vector<unsigned char> temp;
for (size_t i = 0; i < len; i++) {
if (i > 0 && i % 3 == 0) {
// check if the last 3 vals are the rgb values of white, add a 0 alpha
if (data[i] == 255 && data[i - 1] == 255 && data[i - 2] == 255)
temp.push_back(0);
else
// add a 255 alpha, making the black opaque
temp.push_back(255);
temp.push_back(data[i]);
} else {
temp.push_back(data[i]);
}
}
temp.push_back(0);
assert(temp.size() == image->w() * image->h() * 4);
auto new_image_data = new unsigned char[image->w() * image->h() * 4];
memcpy(new_image_data, temp.data(), image->w() * image->h() * 4);
auto new_image = new Fl_RGB_Image(new_image_data, image->w(), image->h(), 4); // account for alpha
return new_image;
}
int main() {
auto win = new Fl_Double_Window(400, 300);
auto box = new Fl_Box(0, 0, 400, 300);
win->end();
win->show();
auto image = get_image(box->w(), box->h());
auto transparent_image = get_transparent_image(image);
delete image;
box->image(transparent_image);
box->redraw();
return Fl::run();
}
The idea is that an Fl_Image_Surface gives an Fl_RGB_Image with 3 channels (r, g, b), no alpha. We manually add the alpha by creating a temporary vector, querying the data (can be optimized if you know the colors you're using by only checking data[i] == 255. The vector is an RAII type whose life ends at the end of the scope, so we just mempcy the data from the vector to a long-lived unsigned char array that we pass to an Fl_RGB_Image specifying the depth to be 4, accounting for alpha.
The other option is to use an external library like CImg (single header lib) to draw text into an image buffer and then pass that buffer to Fl_RGB_Image.
Related
I've been trying to render images to /dev/video. I can get something to sort of display but it's somewhat scrambled.
I first started off trying to render a normal RGB24 image (based off this example https://stackoverflow.com/a/44648382/3818491), but the result (below) was a scrambled image.
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <iostream>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <CImg.h>
#define VIDEO_OUT "/dev/video0" // V4L2 Loopack
#define WIDTH 1280
#define HEIGHT 720
int main() {
using namespace cimg_library;
CImg<uint8_t> canvas(WIDTH, HEIGHT, 1, 3);
const uint8_t red[] = {255, 0, 0};
const uint8_t purple[] = {255, 0, 255};
int fd;
if ((fd = open(VIDEO_OUT, O_RDWR)) == -1) {
std::cerr << "Unable to open video output!\n";
return 1;
}
struct v4l2_format vid_format;
vid_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
if (ioctl(fd, VIDIOC_G_FMT, &vid_format) == -1) {
std::cerr << "Unable to get video format data. Errro: " << errno << '\n';
return 1;
}
size_t framesize = canvas.size();
int width = canvas.width(), height = canvas.height();
vid_format.fmt.pix.width = width;
vid_format.fmt.pix.height = height;
vid_format.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
vid_format.fmt.pix.sizeimage = framesize;
vid_format.fmt.pix.field = V4L2_FIELD_NONE;
if (ioctl(fd, VIDIOC_S_FMT, &vid_format) == -1) {
std::cerr << "Unable to set video format! Errno: " << errno << '\n';
return 1;
}
std::cout << "Stream running!\n";
while (true) {
canvas.draw_plasma();
canvas.draw_rectangle(
100, 100, 100 + 100, 100 + 100, red, 1);
canvas.draw_text(5,5, "Hello World!", purple);
canvas.draw_text(5, 20, "Image freshly rendered with the CImg Library!", red);
write(fd, canvas.data(), framesize);
}
}
So I checked what (I think) /dev/video expects which seems to be YUV420P.
v4l2-ctl --list-formats-ext 130 ↵
ioctl: VIDIOC_ENUM_FMT
Type: Video Capture
[0]: 'YU12' (Planar YUV 4:2:0)
Size: Discrete 1280x720
Interval: Discrete 0.033s (30.000 fps)
So I attempted to convert the frame that format (using this code to quickly test).
Adjusting the spec to:
vid_format.fmt.pix.width = width;
vid_format.fmt.pix.height = height;
vid_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
vid_format.fmt.pix.sizeimage = width*height*3/2; // size of yuv buffer
vid_format.fmt.pix.field = V4L2_FIELD_NONE;
That results in this (which seems to be from what I've gathered the structure of a yuv420 image but still rendered incorrectly).
What does /dev/video0 expect?
After a lot of hacking around, I've managed to generate a valid YUYV video/image to send to /dev/video0.
First I make a buffer to hold the frame:
// Allocate buffer for the YUUV frame
std::vector<uint8_t> buffer;
buffer.resize(vid_format.fmt.pix.sizeimage);
Then I write the current canvas to the buffer in YUYV format.
bool skip = true;
cimg_forXY(canvas, cx, cy) {
size_t row = cy * width * 2;
uint8_t r, g, b, y;
r = canvas(cx, cy, 0);
g = canvas(cx, cy, 1);
b = canvas(cx, cy, 2);
y = std::clamp<uint8_t>(r * .299000 + g * .587000 + b * .114000, 0, 255);
buffer[row + cx * 2] = y;
if (!skip) {
uint8_t u, v;
u = std::clamp<uint8_t>(r * -.168736 + g * -.331264 + b * .500000 + 128, 0, 255);
v = std::clamp<uint8_t>(r * .500000 + g * -.418688 + b * -.081312 + 128, 0, 255);
buffer[row + (cx - 1) * 2 + 1] = u;
buffer[row + (cx - 1) * 2 + 3] = v;
}
skip = !skip;
}
Note:
CImg has RGBtoYUV has an in-place RGB to YUV conversion, but for some reason calling this on a uint8_t canvas just zeros it.
It also has get_YUVtoRGB which (allocates and) returns a CImg<float> canvas, which I think you multiply each value by 255 to scale to a byte, however, whatever I tried that did not give the correct colour. Edit: I likely forgot the +128 bias (though I still prefer not reallocating for each frame)
My full code is here (if anyone wants to do something similar) https://gist.github.com/MacDue/36199c3f3ca04bd9fd40a1bc2067ef72
I want to convert 24bit png images to 8bit png images
I tried several methods, but they failed.
I'd like to convert the color 24-bit png_images to color 8-bit png_images
However, if I try to convert to an 8-bit image, it becomes gray scale.
I want to use an imwrite(). But anything doesn't matter.
Below is my full code.
#include <oppencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp> //for resize
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <stdio.h>
using namespace cv;
using namespace std;
void overlayImage(const Mat &background, const Mat &foreground, Mat &output,
Point2i location);
int main(int argc, char** argv)
{
Mat image_background;
Mat black_background;
Mat image_target, image_segmentation;
image_target = imread("panda.png", IMREAD_UNCHANGED); // Transparent PNG
image_segmentation = imread("panda_segmentation_stroke.png", IMREAD_UNCHANGED);
string filename, filename2;
vector<String> fn;
glob("C:\\Users\\IMRC\\source\\repos\\OpenCVProject\\OpenCVProject\\background\\*.jpg", fn, false);
size_t count = fn.size();
cout << "Image Size " << count << "\n";
float MIN_SIZE = 0.3;
float MAX_SIZE = 0.8;
float WIDTH = 300;
float HEIGHT = 400;
float SIZE_WIDTH, SIZE_HEIGHT, Point_x, Point_y; // random size and point
string JPEGImagesPath = "C:\\Users\\IMRC\\DESKTOP\\TEST\\JPEGImages\\2019-";
string SEG_ImagesPath = "C:\\Users\\IMRC\\DESKTOP\\TEST\\SegmentationClass\\2019-";
srand(static_cast <unsigned> (time(0)));
black_background = imread(fn[0], IMREAD_COLOR);
resize(black_background, black_background, Size(500, 500));
for (size_t i = 0; i < count; i++) {
cout << fn[i] << "\n";
image_background = imread(fn[i], IMREAD_COLOR);
black_background.setTo(Scalar(0, 0, 0));
resize(image_background, image_background, Size(500,500)); // background image resize
Mat image_resize_target;
Mat image_resize_segmentation;
SIZE_WIDTH = MIN_SIZE + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX / (MAX_SIZE - MIN_SIZE)));
SIZE_HEIGHT = MIN_SIZE + static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / (MAX_SIZE - MIN_SIZE)));
Point_x = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / WIDTH));
Point_y = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / HEIGHT));
resize(image_target, image_resize_target, Size(), SIZE_WIDTH, SIZE_HEIGHT);
resize(image_segmentation, image_resize_segmentation, Size(), SIZE_WIDTH, SIZE_HEIGHT);
overlayImage(image_background, image_resize_target, image_background, cv::Point(Point_x, Point_y));
overlayImage(black_background, image_resize_segmentation, black_background, cv::Point(Point_x, Point_y));
stringstream JPEGImages, SEG_Images, SEG_RawImage;
JPEGImages << JPEGImagesPath << i + 1 << ".jpg";
SEG_Images << SEG_ImagesPath << i + 1 << ".png";
filename = JPEGImages.str();
imwrite(filename, image_background); // save JPEGImages
filename2 = SEG_Images.str();
imwrite(filename2, black_background); // save SegmentationClass
}
return 0;
}
void overlayImage(const Mat &background, const Mat &foreground, Mat &output, Point2i location)
{
background.copyTo(output);
// start at the row indicated by location, or at row 0 if location.y is negative.
for (int y = std::max(location.y, 0); y < background.rows; ++y)
{
int fY = y - location.y; // because of the translation
if (fY >= foreground.rows) // we are done of we have processed all rows of the foreground image.
break;
// start at the column indicated by location,
// or at column 0 if location.x is negative.
for (int x = std::max(location.x, 0); x < background.cols; ++x)
{
int fX = x - location.x; // because of the translation.
if (fX >= foreground.cols) // we are done with this row if the column is outside of the foreground image.
break;
// determine the opacity of the foregrond pixel, using its fourth (alpha) channel.
double opacity = ((double)foreground.data[fY * foreground.step + fX * foreground.channels() + 3]) / 255.;
// and now combine the background and foreground pixel, using the opacity,
// but only if opacity > 0.
for (int c = 0; opacity > 0 && c < output.channels(); ++c)
{
unsigned char foregroundPx = foreground.data[fY * foreground.step + fX * foreground.channels() + c];
unsigned char backgroundPx = background.data[y * background.step + x * background.channels() + c];
output.data[y*output.step + output.channels()*x + c] = backgroundPx * (1. - opacity) + foregroundPx * opacity;
}
}
}
}
The purpose of this code is to synthesize.
After preparing background images and another png_images, And exporting the composed image.
I want to print this image as an 8 bit colored png image.
How do I modify the source code?
Add Picture
enter image description here
You can use Mat::convertTo function to change the type of your cv::Mat. I am assuming that the image that you want to convert to an 8-bit one is of type CV_32SC3 (or CV_32SC4 if you have an alpha channel). Even if my guess is not correct, you can learn the right image type using cv::Mat::type(). You can then convert your image to a CV_8UC3 using the first function above. Note that the conversion function accepts a scaling factor, alpha. This should be set properly, which would otherwise cause integer overflow. You can figure out the right scaling factor according to what cv::Mat::type() gives you. Hope this helps!
Edit : You can check what the result of type() means here.
Synopsis of cv::imwrite says that the only parameter you can tweak is the image quality when writing a PNG file. Changing the number of channels of your OpenCV image is the only second way to set the image properties which we already discussed above. As a result, obtaining an 8-bit color PNG is possible only through using a palette. Check libpng's documentation where it says If you are writing an indexed colors image, you should provide a palette (colormap).
I am trying to port the pixel perfect collision detection in Cocos2d-x the original version was made for Cocos2D and can be found here: http://www.cocos2d-iphone.org/forums/topic/pixel-perfect-collision-detection-using-color-blending/
Here is my code for the Cocos2d-x version
bool CollisionDetection::areTheSpritesColliding(cocos2d::CCSprite *spr1, cocos2d::CCSprite *spr2, bool pp, CCRenderTexture* _rt) {
bool isColliding = false;
CCRect intersection;
CCRect r1 = spr1->boundingBox();
CCRect r2 = spr2->boundingBox();
intersection = CCRectMake(fmax(r1.getMinX(),r2.getMinX()), fmax( r1.getMinY(), r2.getMinY()) ,0,0);
intersection.size.width = fmin(r1.getMaxX(), r2.getMaxX() - intersection.getMinX());
intersection.size.height = fmin(r1.getMaxY(), r2.getMaxY() - intersection.getMinY());
// Look for simple bounding box collision
if ( (intersection.size.width>0) && (intersection.size.height>0) ) {
// If we're not checking for pixel perfect collisions, return true
if (!pp) {
return true;
}
unsigned int x = intersection.origin.x;
unsigned int y = intersection.origin.y;
unsigned int w = intersection.size.width;
unsigned int h = intersection.size.height;
unsigned int numPixels = w * h;
//CCLog("Intersection X and Y %d, %d", x, y);
//CCLog("Number of pixels %d", numPixels);
// Draw into the RenderTexture
_rt->beginWithClear( 0, 0, 0, 0);
// Render both sprites: first one in RED and second one in GREEN
glColorMask(1, 0, 0, 1);
spr1->visit();
glColorMask(0, 1, 0, 1);
spr2->visit();
glColorMask(1, 1, 1, 1);
// Get color values of intersection area
ccColor4B *buffer = (ccColor4B *)malloc( sizeof(ccColor4B) * numPixels );
glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
_rt->end();
// Read buffer
unsigned int step = 1;
for(unsigned int i=0; i 0 && color.g > 0) {
isColliding = true;
break;
}
}
// Free buffer memory
free(buffer);
}
return isColliding;
}
My code is working perfectly if I send the "pp" parameter as false. That is if I do only a bounding box collision but I am not able to get it working correctly for the case when I need Pixel Perfect collision.
I think the opengl masking code is not working as I intended.
Here is the code for "_rt"
_rt = CCRenderTexture::create(visibleSize.width, visibleSize.height);
_rt->setPosition(ccp(origin.x + visibleSize.width * 0.5f, origin.y + visibleSize.height * 0.5f));
this->addChild(_rt, 1000000);
_rt->setVisible(true); //For testing
I think I am making a mistake with the implementation of this CCRenderTexture
Can anyone guide me with what I am doing wrong ?
Thank you for your time :)
Finally solved the problem.
Had to use custom opengl fragment shaders to shade one of the sprites completely RED and the other completely BLUE and then looping through glReadPixels values to find any pixel having both RED and BLUE pixels. (Blending has to be considered as well, we do not want to replace one pixel value by the other)
In-Depth information can be found on my blog post
http://blog.muditjaju.infiniteeurekas.in/?p=1
You are not stepping through the buffer properly.
// Read buffer
unsigned int step = 1;
for(unsigned int i=0; i<numPixels; i+=step)
{
ccColor4B color = buffer;
if (color.r > 0 && color.g > 0)
{
isCollision = YES;
break;
}
}
source: http://www.cocos2d-iphone.org/forums/topic/pixel-perfect-collision-detection-using-color-blending/#post-337907
I have made a static library Win32 (it doesn't actually contain Win32 code) library in MSVC 2008 and I am currently trying to link to it in QT Creator. But whenever I compile, I get the error:
C:\Users\Snowball\Documents\QT Creator\Libraries\FontSystem\main.cpp:-1: error: undefined reference to `NGUI::CFont::CFont()'
The library is a font system that loads a PNG using FreeImage and then "cuts" it up into individual symbols then passes the image data to gluBuild2DMipMaps() which then creates an OpenGL texture out of it to be used later on when drawing strings. I have ALL my class methods defined and the whole class is part of a namespace called NGUI. This way the font system won't be confused with another if for some reason two are in use. To link this library, I simply added the following code to my .pro file: LIBS += FontSystem.lib
and the only file in the application right now is this:
#include "fontsystem.h"
using namespace NGUI;
int main(int argc, char *argv[])
{
cout<< "Starting the FontSystem..."<< endl;
CFont *cFont = new CFont();
cout<< "FontSystem Started!"<< endl;
system("sleep 1");
return 0;
}
The file fontsystem.h looks like this:
#ifndef FONTSYSTEM_H
#define FONTSYSTEM_H
// Include the Basic C++ headers.
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <assert.h>
#include <limits>
using namespace std;
// Include the OpenGL and Image Headers.
#include <GL/gl.h>
#include <GL/glu.h>
#include "utilities.h"
// Macros.
#define DIM(x) (sizeof(x)/sizeof(*(x)))
/*
The Font System works by loading all the font images (max image size 32px^2) into memory and storing
the OpenGL texture ID's into an array that can be access at all times. The DrawString() functions will
search through the string for the specified character requested to draw and then it will draw a quad
and paint the texture on it. Since all the images are pre-loaded, no loading of the images at load time
is necessary, this is memory consuming but efficiant for the CPU. ALL functions WILL return a character
string specifing errors or success. A function will work as long as it can and when an error happens,
unless the error is fatal, the functions will NOT rollback changes! This ensures that during testing, a
very certain bug can be spotted.
*/
namespace NGUI // This namespace is used to ensure no confusion happens. This font system paints 2D fonts for GUIs.
{
class CFont
{
public:
CFont();
~CFont();
template<typename tChar> char* DrawString(tChar *apString, int aiSize, int aiX, int aiY);
template<typename tNum> char* DrawString(tNum anNumber, int aiSize, int aiX, int aiY);
private:
char* SetupFont(); // This function will load as many images as possible into memory.
GLuint miTextIDs[36];
int miDrawIDs[1024];
};
}
#endif // FONTSYSTEM_H
EDIT:
Here is the implementation file for fontsystem.h
#include "fontsystem.h"
#include "fontsystem.h"
namespace NGUI
{
CFont::CFont()
{
SetupFont();
}
CFont::~CFont() {}
template<typename tChar>
char* CFont::DrawString(tChar *apString, int aiSize, int aiX, int aiY)
{
// Search the string from most significant character to least significant.
int iSelectIndex = 0;
for(size_t i = 0; apString[i] != NULL; ++i)
{
iSelectIndex = apString[i] >= '0' && apString[i] <= '9' ? (apString[i] - '0') :
apString[i] >= 'A' && apString[i] <= 'Z' ? (apString[i] - 'A' + 10) :
apString[i] >= 'a' && apString[i] <= 'z' ? (apString[i] - 'a' + 10) :
apString[i] == ' ' ? 36 : // This is a special case, This see's if the current character is a space or not.
-1;
if(iSelectIndex == -1)
{
return "The String Is Corrupt! Aborting!";
}
// Add the current selected character to the drawing array.
miDrawIDs[i] = iSelectIndex;
}
// Go through and draw each and every character.
for(size_t i = 0; i < DIM(miDrawIDs); ++i)
{
// Paint each qaud with the X,Y coordinates. After each quad has been successfully drawn,
// Add the size to the X coordinate. NOTE: Each character is square!!!
if(miDrawIDs[i] != 36)
{
glBindTexture(GL_TEXTURE_2D, miDrawIDs[i]);
}
// The font color is always white.
glColor4f(1.0, 1.0, 1.0, 0.0); // The alpha argument in the function call is set to 0 to allow color only where image data is present.
glBegin(GL_QUADS);
glTexCoord2i(0, 0);
glVertex2i(aiX, aiY);
glTexCoord2i(1, 0);
glVertex2i(aiX + aiSize, aiY);
glTexCoord2i(1, 1);
glVertex2i(aiX + aiSize, aiY + aiSize);
glTexCoord2i(0, 1);
glVertex2i(aiX, aiY + aiSize);
glEnd();
// Now, increase the X position by the size.
aiX += aiSize;
}
return "Successful Drawing of String!";
}
template<typename tNum>
char* CFont::DrawString(tNum anNumber, int aiSize, int aiX, int aiY)
{
// Convert the supplied number to a character string via snprintf().
char *vTempString = new char[1024];
snprintf(vTempString, 1024, "%f", anNumber);
// Next, run DrawString().
return DrawString<char>(vTempString, aiSize, aiX, aiY);
}
char* CFont::SetupFont()
{
// First Load The PNG file holding the font.
FreeImage_Initialise(false);
FIBITMAP *spBitmap = FreeImage_Load(FIF_PNG, "Font.png", BMP_DEFAULT);
if(!spBitmap)
{
return "Was Unable To Open/Decode Bitmap!";
}
// Do an image sanity check.
if(!FreeImage_HasPixels(spBitmap))
{
return "The Image doesn't contain any pixel data! Aborting!";
}
// The Image will have the red and blue channel reversed, so we need to correct them.
SwapRedBlue32(spBitmap);
// Retrieve all the image data from FreeImage.
unsigned char *pData = FreeImage_GetBits(spBitmap);
int iWidth = FreeImage_GetWidth(spBitmap);
// Cutup the PNG.
int iFontElementSize = (32*32)*4; // The first two numbers, are the dimensions fo the element, the last number (4) is the number of color channels (Red Green Blue and Alpha)
bool bDone = false; // This bit is only set when the entire image has been loaded.
unsigned char *pElemBuff = new unsigned char[iFontElementSize]; // The temporary element buffer.
int iDataSeek = 4; // Start with an offset of 4 because the first byte of image data starts there.
int iTexIdx = 0; // This is an offset specifing which texture to create/bind to.
// Create all 36 OpenGL texures. 0-9 and A-Z and finally space (' ')
glGenTextures(37, miTextIDs);
while(!bDone)
{
// Now load the an element into the buffer.
for(int i = 0, iXCount = 0, iYCount = 0;
i < iFontElementSize; ++i, ++iXCount)
{
if(iXCount >= (32*4))
{
iXCount = 0; // Reset the column offset.
++iYCount; // Move down 1 row.
iDataSeek += ((iWidth * 4) - (32*4)); // Set the data seek to the next corrosponding piece of image data.
}
if(pData[iDataSeek] == NULL)
{
break;
}
pElemBuff[i] = pData[iDataSeek];
}
// Check to see if we are done loading to prevent memory corruption and leakage.
if(bDone || iTexIdx >= 37)
{
break;
}
// Create The OpenGL Texture with the current Element.
glBindTexture(GL_TEXTURE_2D, miTextIDs[iTexIdx]);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, 32, 32, GL_RGBA, GL_UNSIGNED_BYTE, pElemBuff);
// Create the correct texture envirnment to the current texture.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
}
// Do a little house cleaning!
delete[] pElemBuff;
delete pData;
FreeImage_Unload(spBitmap);
FreeImage_DeInitialise();
}
}
PLEASE NOTE: this code hasn't been tested yet but compile fine (according to MSVC 2008)
I forgot to change the system("sleep 1"); to system("PAUSE"); and the reason I had it invoking that command is because I originally was building this in linux.
EDIT 2: I have updated the implementation code to reflect the file.
I'm trying to get the Kinect depth camera pixels to overlay onto the RGB camera. I am using the C++ Kinect 1.0 SDK with an Xbox Kinect, OpenCV and trying to use the new "NuiImageGetColorPixelCoordinateFrameFromDepthPixelFrameAtResolution" method.
I have watched the image render itself in slow motion and looks as if pixels are being drawn multiple times in the one frame. It first draws itself from the top and left borders, then it gets to a point (you can see a 45 degree angle in there) where it starts drawing weird.
I have been trying to base my code off of the C# code written by Adam Smith at the MSDN forums but no dice. I have stripped out the overlay stuff and just want to draw the depth normalized depth pixels where it "should" be in the RGB image.
The image on the left is what I'm getting when trying to fit the depth image to RGB space, and the image on the right is the "raw" depth image as I like to see it. I was hoping this my method would create a similar image to the one on the right with slight distortions.
This is the code and object definitions that I have at the moment:
// From initialization
INuiSensor *m_pNuiInstance;
NUI_IMAGE_RESOLUTION m_nuiResolution = NUI_IMAGE_RESOLUTION_640x480;
HANDLE m_pDepthStreamHandle;
IplImage *m_pIplDepthFrame;
IplImage *m_pIplFittedDepthFrame;
m_pIplDepthFrame = cvCreateImage(cvSize(640, 480), 8, 1);
m_pIplFittedDepthFrame = cvCreateImage(cvSize(640, 480), 8, 1);
// Method
IplImage *Kinect::GetRGBFittedDepthFrame() {
static long *pMappedBits = NULL;
if (!pMappedBits) {
pMappedBits = new long[640*480*2];
}
NUI_IMAGE_FRAME pNuiFrame;
NUI_LOCKED_RECT lockedRect;
HRESULT hr = m_pNuiInstance->NuiImageStreamGetNextFrame(m_pDepthStreamHandle, 0, &pNuiFrame);
if (FAILED(hr)) {
// return the older frame
return m_pIplFittedDepthFrame;
}
bool hasPlayerData = HasSkeletalEngine(m_pNuiInstance);
INuiFrameTexture *pTexture = pNuiFrame.pFrameTexture;
pTexture->LockRect(0, &lockedRect, NULL, 0);
if (lockedRect.Pitch != 0) {
cvZero(m_pIplFittedDepthFrame);
hr = m_pNuiInstance->NuiImageGetColorPixelCoordinateFrameFromDepthPixelFrameAtResolution(
m_nuiResolution,
NUI_IMAGE_RESOLUTION_640x480,
640 * 480, /* size is previous */ (unsigned short*) lockedRect.pBits,
(640 * 480) * 2, /* size is previous */ pMappedBits);
if (FAILED(hr)) {
return m_pIplFittedDepthFrame;
}
for (int i = 0; i < lockedRect.size; i++) {
unsigned char* pBuf = (unsigned char*) lockedRect.pBits + i;
unsigned short* pBufS = (unsigned short*) pBuf;
unsigned short depth = hasPlayerData ? ((*pBufS) & 0xfff8) >> 3 : ((*pBufS) & 0xffff);
unsigned char intensity = depth > 0 ? 255 - (unsigned char) (256 * depth / 0x0fff) : 0;
long
x = pMappedBits[i], // tried with *(pMappedBits + (i * 2)),
y = pMappedBits[i + 1]; // tried with *(pMappedBits + (i * 2) + 1);
if (x >= 0 && x < m_pIplFittedDepthFrame->width && y >= 0 && y < m_pIplFittedDepthFrame->height) {
m_pIplFittedDepthFrame->imageData[x + y * m_pIplFittedDepthFrame->widthStep] = intensity;
}
}
}
pTexture->UnlockRect(0);
m_pNuiInstance->NuiImageStreamReleaseFrame(m_pDepthStreamHandle, &pNuiFrame);
return(m_pIplFittedDepthFrame);
}
Thanks
I have found that the problem was that the loop,
for (int i = 0; i < lockedRect.size; i++) {
// code
}
was iterating on a per-byte basis, not on a per-short (2 bytes) basis. Since lockedRect.size returns the number of bytes the fix was simply changing the increment to i += 2, even better would be changing it to sizeof(short), like so,
for (int i = 0; i < lockedRect.size; i += sizeof(short)) {
// code
}