Related
Currently I am working on a program that is able to draw a spline based on four points. As can be seen below, I currently have it so that you need to manually enter these points into the code. I am hoping to change that using mouse clicks. What I intend to do is have the user be able to click on the image, which draws a dot immediately. This will be able to be done as many times as the user wants, but it will draw a spline when every four points are drawn. I am running into two problems. Firstly when trying to implement a simple mouse click to just draw a circle in onMouse, it is not working. Secondly, I am wondering how I will be able to store the coordinates of each mouse click as they are happening, so I can create a loop that plugs these coordinates into the below matrix equations to draw a spline while the program is running. I have an idea of how I can store the first, but I am not sure how to handle multiple coordinates as well as how to have a definite variable name for each that I can put into the equations. Apologies for the long post, I have some big aspirations for this project despite having limited experience, so I would appreciate some guidance.
#include <iostream>
#include<opencv2/core/core.hpp> //Mat is defined there
#include<opencv2/imgproc/imgproc.hpp> //resize an image
#include<opencv2/highgui/highgui.hpp> //input or output: imread(), imshow()
using namespace std;
using namespace cv;
Mat img;
void onMouse(int event, int x, int y, int flags, void* param)
{
if (event == EVENT_LBUTTONDOWN)
{
printf("(%d, %d)\n", x, y);
int r = 1;
circle(img, Point(x, y), r, Scalar(0, 255, 100), 5);
}
}
int main(int argc, char** argv)
{
img.create(600, 800, CV_8UC3);
img = Scalar(255, 255, 255);
double a_values[3][2] = { { 2.0, 1 }, { 0.0, 3.0 }, { -2.0, -4.0 } };
//A.create(3, 2, CV_64FC1); //create a matrix 3x2 with double value
Mat A = Mat(3, 2, CV_64FC1, a_values); //Constructor: pass the values directly using a 2D array.
printf("matrix A:\n");
cout << A << endl;
Mat B;
B.create(2, 2, CV_64FC1); //2x2 matrix
B.ptr<double>(0)[0] = 1.0;
B.ptr<double>(0)[1] = 2.0;
B.ptr<double>(1)[0] = 0.0;
B.ptr<double>(1)[1] = -2.0;
printf("matrix B:\n");
cout << B << endl;
Mat C = A * B; //Matrix product
printf("matrix C:\n");
cout << C << endl;
Mat B_inv = B.inv();
printf("matrix B inverse:\n");
cout << B_inv << endl;
double m_values[4][4] = { { 0, 0, 0, 1 }, { 1, 1, 1, 1 }, { 0, 0, 1, 0 }, { 3, 2, 1, 0 } };
Mat M = Mat(4, 4, CV_64FC1, m_values);
Mat M_inv = M.inv();
printf("matrix M inverse:\n");
cout << M_inv << endl;
double h_values[4][4] = { { 2, -2, 1, 1 }, { -3, 3, -2, -1 }, { 0, 0, 1, 0 }, { 1, 0, 0, 0 } };
Mat Hermite = Mat(4, 4, CV_64FC1, h_values);
double point_values[4][2] = { { 200, 350 }, { 220, 400 }, { 600, 300 }, { 390, 300 } };
Mat Points = Mat(4, 2, CV_64FC1, point_values);
Mat Final = Hermite * Points;
printf("Final matrix:\n");
cout << Final << endl;
/* If there are two points P1(30, 50) and P2(80, 120), I want to draw a spline between
them and also make sure the speed of the spline at point P1 equals(500, 2) and at P2 equals(10, 1000). */
//Draw 1st point
circle(img, Point(200, 350), 1, Scalar(0, 0, 255), 3);
//Draw 2nd point
circle(img, Point(220, 400), 1, Scalar(0, 0, 255), 3);
circle(img, Point(400, 450), 1, Scalar(0, 0, 255), 3);
circle(img, Point(350, 500), 1, Scalar(0, 0, 255), 3);
//Draw the spline between 1st and 2nd points
//Use a loop on t [0, 1], for different t values, compute x(t), y(t); then use circle() to draw it
// x(t) = axt3 + bxt2 + cxt + dx
// y(t) = ayt3 + byt2 + cyt + dy
double ax = (int)(Final.at<double>(0, 0));
double ay = (int)(Final.at<double>(0, 1));
double bx = (int)(Final.at<double>(1, 0));
double by = (int)(Final.at<double>(1, 1));
double cx = (int)(Final.at<double>(2, 0));
double cy = (int)(Final.at<double>(2, 1));
double dx = (int)(Final.at<double>(3, 0));
double dy = (int)(Final.at<double>(3, 1));
printf("ax:\n");
cout << ax << endl;
printf("dx:\n");
cout << dx << endl;
for (double t = 0.0; t <= 1.0; t += 0.001)
{
int x = ax * t * t * t + bx * t * t + cx * t + dx;
int y = ay * t * t * t + by * t * t + cy * t + dy;
circle(img, Point(x, y), 1, Scalar(0, 0, 0), 1);
}
while (1)
{
imshow("Spline", img);
char c = waitKey(1);
if (c == 27)
break;
}
return 1;
}
You defined onMouse function, but you did not register it to any window. You need to create window with cv::namedWindow and then register your callback for mouse with cv::setMouseCallback. In your case, just add this before anything in your main function:
cv::namedWindow("Spline");
cv::setMouseCallback("Spline", onMouse);
Here is the simple program to draw circle/point to provided image. It will store point where you clicked and it will draw all points to the image.
#include <opencv2/opencv.hpp>
std::vector<cv::Point> points;
void onMouse(int action, int x, int y, int, void*) {
if (action == cv::EVENT_LBUTTONDOWN) {
points.push_back(cv::Point{x, y});
}
}
int main(int argc, char** argv) {
const auto mainWindow = "Main Window";
cv::namedWindow(mainWindow);
cv::setMouseCallback(mainWindow, onMouse);
cv::Mat image {600, 800, CV_8UC3, cv::Scalar{255, 255, 255}};
while (true) {
for (const auto& point : points) {
cv::circle(image, point, 5, cv::Scalar{0, 200, 0}, -1);
}
cv::imshow(mainWindow, image);
cv::waitKey(25);
}
cv::waitKey();
cv::destroyAllWindows();
return 0;
}
EDIT:
Be aware that this solution will re-render old points. You can improve this by drawing just new points.
When you click, add that point to temporary vector and when you draw all points from temporary vector, clear that vector.
For more improvement, you can check if vector is not empty and than call cv::imshow.
Trying to convert 32,24,16,8 bit images to their grayscale presentation. I read about using BitBlt, but maybe exist some light way built-in opportunity
in GDI+?
Code:
#include <vector>
...
class gdiplus_init
{
ULONG_PTR token;
public:
gdiplus_init()
{
Gdiplus::GdiplusStartupInput tmp;
Gdiplus::GdiplusStartup(&token, &tmp, NULL);
}
~gdiplus_init()
{
Gdiplus::GdiplusShutdown(token);
}
};
bool getbits(const wchar_t *filename, Gdiplus::PixelFormat pixelformat,
std::vector<BYTE> &bitmapinfo, std::vector<BYTE> &bits, int &w, int &h)
{
gdiplus_init init;
WORD bpp = 0;
int usage = DIB_RGB_COLORS;
int palettesize = 0;
switch(pixelformat)
{
case PixelFormat8bppIndexed:
bpp = 8;
usage = DIB_PAL_COLORS;
palettesize = 256 * sizeof(RGBQUAD);
break;
case PixelFormat16bppRGB555: bpp = 16; break;
case PixelFormat16bppRGB565: bpp = 16; break;
case PixelFormat24bppRGB: bpp = 24; break;
case PixelFormat32bppRGB: bpp = 32; break;
default:return false;
}
auto src = Gdiplus::Bitmap::FromFile(filename);
if(src->GetLastStatus() != Gdiplus::Status::Ok)
return false;
auto dst = src->Clone(0, 0, src->GetWidth(), src->GetHeight(),
pixelformat);
w = src->GetWidth();
h = src->GetHeight();
HBITMAP hbitmap;
Gdiplus::Color color;
dst->GetHBITMAP(color, &hbitmap);
//allocate enough memory for bitmapinfo and initialize to zero
//it's sizeof BITMAPINFO structure + size of palette
bitmapinfo.resize(sizeof(BITMAPINFO) + palettesize, 0);
//fill the first 6 parameters
BITMAPINFO* ptr = (BITMAPINFO*)bitmapinfo.data();
ptr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); //don't skip
ptr->bmiHeader.biWidth = w;
ptr->bmiHeader.biHeight = h;
ptr->bmiHeader.biPlanes = 1;
ptr->bmiHeader.biBitCount = bpp;
ptr->bmiHeader.biCompression = BI_RGB;
//magic formula to calculate the size:
//this is roughly w * h * bytes_per_pixel, it's written this way
//to account for "bitmap padding"
DWORD size = ((w * bpp + 31) / 32) * 4 * h;
//allocate memory for image
bits.resize(size, 0);
//finally call GetDIBits to fill bits and bitmapinfo
HDC hdc = GetDC(0);
GetDIBits(hdc, hbitmap, 0, h, &bits[0], (BITMAPINFO*)&bitmapinfo[0], usage);
ReleaseDC(0, hdc);
//cleanup
delete src;
delete dst;
return true;
}
void CMFCApplicationColorsView::OnDraw(CDC* pDC)
{
...
std::vector<BYTE> bi; //automatic storage
std::vector<BYTE> bits;
int w, h;
//24-bit test
if(getbits(L"c:\\test\\24bit.bmp", PixelFormat24bppRGB, bi, bits, w, h))
StretchDIBits(dc, 0, 0, w, h, 0, 0, w, h,
bits.data(), (BITMAPINFO*)bi.data(), DIB_RGB_COLORS, SRCCOPY);
//8-bit test
if(getbits(L"c:\\test\\8bit.bmp", PixelFormat8bppIndexed, bi, bits, w, h))
StretchDIBits(dc, 0, 220, w, h, 0, 0, w, h,
bits.data(), (BITMAPINFO*)bi.data(), DIB_PAL_COLORS, SRCCOPY);
}
You can draw the GDI+ directly with various transformation. Use Gdiplus::Graphics to draw on device context.
For grayscale conversion, RGB values all have to be the same. Gdiplus::ColorMatrix can transform the colors. Green is usually more important, it gets more weight.
void draw(CDC *pdc)
{
//this line should be in OnCreate or somewhere other than paint routine
Gdiplus::Bitmap source(L"file.jpg");
//gray scale conversion:
Gdiplus::ColorMatrix matrix =
{
.3f, .3f, .3f, 0, 0,
.6f, .6f, .6f, 0, 0,
.1f, .1f, .1f, 0, 0,
0, 0, 0, 1, 0,
0, 0, 0, 0, 1
};
Gdiplus::ImageAttributes attr;
attr.SetColorMatrix(&matrix,
Gdiplus::ColorMatrixFlagsDefault, Gdiplus::ColorAdjustTypeBitmap);
Gdiplus::Graphics gr(pdc->GetSafeHdc());
Gdiplus::REAL w = (Gdiplus::REAL)source.GetWidth();
Gdiplus::REAL h = (Gdiplus::REAL)source.GetHeight();
Gdiplus::RectF rect(0, 0, w, h);
gr.DrawImage(&source, rect, 0, 0, w, h, Gdiplus::UnitPixel, &attr);
}
Note, I used rough values for grayscale matrix. See the answer mentioned in comment for a better matrix.
To convert the file, the process is similar, except use Gdiplus::Graphics to create memory dc and save it.
int GetEncoderClsid(const WCHAR* format, CLSID* clsid)
{
int result = -1;
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
Gdiplus::GetImageEncodersSize(&num, &size);
if(size)
{
Gdiplus::ImageCodecInfo* codec = (Gdiplus::ImageCodecInfo*)(malloc(size));
GetImageEncoders(num, size, codec);
for(UINT j = 0; j < num; ++j)
if(wcscmp(codec[j].MimeType, format) == 0)
{
*clsid = codec[j].Clsid;
result = j;
}
free(codec);
}
return result;
}
bool convert_grayscale(const wchar_t *file_in, const wchar_t *file_out)
{
CStringW extension = PathFindExtensionW(file_out);
extension.Remove(L'.');
extension.MakeLower();
if(extension == L"jpg") extension = L"jpeg";
extension = L"image/" + extension;
CLSID clsid;
if(GetEncoderClsid(extension, &clsid) == -1)
return false;
Gdiplus::Bitmap source(file_in);
if(source.GetLastStatus() != Gdiplus::Status::Ok)
return false;
Gdiplus::REAL w = (Gdiplus::REAL)source.GetWidth();
Gdiplus::REAL h = (Gdiplus::REAL)source.GetHeight();
Gdiplus::RectF rect(0, 0, w, h);
Gdiplus::Bitmap copy((INT)w, (INT)h, source.GetPixelFormat());
Gdiplus::ColorMatrix matrix =
{
.3f, .3f, .3f, 0, 0,
.6f, .6f, .6f, 0, 0,
.1f, .1f, .1f, 0, 0,
0, 0, 0, 1, 0,
0, 0, 0, 0, 1
};
Gdiplus::ImageAttributes attr;
attr.SetColorMatrix(&matrix,
Gdiplus::ColorMatrixFlagsDefault, Gdiplus::ColorAdjustTypeBitmap);
Gdiplus::Graphics gr(©);
gr.DrawImage(&source, rect, 0, 0, w, h, Gdiplus::UnitPixel, &attr);
auto st = copy.Save(file_out, &clsid);
return st == Gdiplus::Status::Ok;
}
...
convert_grayscale(L"source.jpg", L"destination.jpg");
If I apply a Sobel filter to an image in Python using scipy.ndimage.filters.convole I get meaningful results, for example, for this simple input image img
0 255 0
0 255 0
0 255 0
the convolution
dimage.filters.convolve(img, Kx)
with Kx
-1 0 1
-2 0 2
-1 0 1
returns a meaningful gradient in x-direction:
-1020 0 1020
-1020 0 1020
-1020 0 1020
I don't know how to get a equivalent result using openCV2 in C++ though. When I define the input image by
int image_data[9] = {0, 255, 0, 0, 255, 0, 0, 255, 0};
cv::Mat image = cv::Mat(3, 3, CV_32F, image_data);
and apply the kernel by
cv::Mat gradientx;
double sobelx_data[9] = {-1, 0, 1, -2, 0, 2, -1, 0, 1};
cv::Mat sobelx = cv::Mat(3, 3, CV_32F, sobelx_data);
cv::filter2D(image, gradientx, -1, sobelx);
I get the following result from
for(int row=0; row<gradientx.rows; row++) {
for(int col=0; col<gradientx.cols; col++) {
std::cout << gradientx.at<int>(row,col) << std::endl;
}
}
it returns the following image
478 -2147482660 478
478 -2147482660 478
478 -2147482660 478
There seems to be an overflow problem, but I don't know why. Trying to get values from gradientx.at<double>(row,col) produces
-1.68911e-311 8.10602e-312 8.11663e-312
-1.68911e-311 8.10602e-312 8.11663e-312
-1.68911e-311 2.122e-314 8.54412e-72
Can someone tell me why this is? Isn't filter2D supposed to do a 2D convolution on the image, and why do I get weird values when addressing the output pixels with <double>? Thank you.
Okay, here's your code with the types corrected (I've also added more parameters to filter2D):
float image_data[9] = {0, 255, 0, 0, 255, 0, 0, 255, 0};
cv::Mat image = cv::Mat(3, 3, CV_32F, image_data);
std::cout << "image = " << std::endl << image << std::endl;
cv::Mat gradientx;
float sobelx_data[9] = {-1, 0, 1, -2, 0, 2, -1, 0, 1};
cv::Mat sobelx = cv::Mat(3, 3, CV_32F, sobelx_data);
std::cout << "sobelx = " << std::endl << sobelx << std::endl;
cv::filter2D(image, gradientx, -1, sobelx, cv::Point(-1, -1), 0,
cv::BORDER_DEFAULT);
std::cout << "gradientx = " << std::endl << gradientx << std::endl;
The result is:
image =
[0, 255, 0;
0, 255, 0;
0, 255, 0]
sobelx =
[-1, 0, 1;
-2, 0, 2;
-1, 0, 1]
gradientx =
[0, 0, 0;
0, 0, 0;
0, 0, 0]
If you look at the top of the documentation page on filtering, you'll see all of the border types that OpenCV uses. By default, filter2D uses BORDER_REFLECT_101. This is probably not what we want, so let's change it to BORDER_REPLICATE.
cv::filter2D(image, gradientx, -1, sobelx, cv::Point(-1, -1), 0,
cv::BORDER_REPLICATE);
Result:
image =
[0, 255, 0;
0, 255, 0;
0, 255, 0]
sobelx =
[-1, 0, 1;
-2, 0, 2;
-1, 0, 1]
gradientx =
[1020, 0, -1020;
1020, 0, -1020;
1020, 0, -1020]
That's better, but the values are flipped. If you look at the bottom of the function description for filter2D you'll see that it actually computes the cross correlation rather than the convolution. So we need to flip the kernel to get the correct results.
cv::Mat sobelxflip;
cv::flip(sobelx, sobelxflip, -1);
cv::filter2D(image, gradientx, -1, sobelxflip, cv::Point(-1, -1), 0,
cv::BORDER_REPLICATE);
std::cout << "gradientx = " << std::endl << gradientx << std::endl;
Result:
gradientx =
[-1020, 0, 1020;
-1020, 0, 1020;
-1020, 0, 1020]
I want to use convexity defects of a human hand as input to a classifier. I want to do this to detect hand gestures (sign language alphabet). Can someone please help me.
The code below is available on the opencv documentation. I want to know how to use this for my the purpose stated above.
// Data for visual representation
int width = 512, height = 512;
Mat image = Mat::zeros(height, width, CV_8UC3);
// Set up training data
float labels[4] = { 1.0, -1.0, -1.0, -1.0 };
Mat labelsMat(4, 1, CV_32FC1, labels);
// Storing as Mat objects of floats
float trainingData[4][2] = { { 501, 10 }, { 255, 10 }, { 501, 255 }, { 10, 501 } };
Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
// Set up SVM's parameters
CvSVMParams params;
params.svm_type = CvSVM::C_SVC;
params.kernel_type = CvSVM::LINEAR;
params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
// Train the SVM
CvSVM SVM;
SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);
Vec3b green(0, 255, 0), blue(255, 0, 0);
// Show the decision regions given by the SVM
for (int i = 0; i < image.rows; ++i)
for (int j = 0; j < image.cols; ++j)
{
Mat sampleMat = (Mat_<float>(1, 2) << j, i);
float response = SVM.predict(sampleMat);
if (response == 1)
image.at<Vec3b>(i, j) = green;
else if (response == -1)
image.at<Vec3b>(i, j) = blue;
}
// Show the training data
int thickness = -1;
int lineType = 8;
circle(image, Point(501, 10), 5, Scalar(0, 0, 0), thickness, lineType);
circle(image, Point(255, 10), 5, Scalar(255, 255, 255), thickness, lineType);
circle(image, Point(501, 255), 5, Scalar(255, 255, 255), thickness, lineType);
circle(image, Point(10, 501), 5, Scalar(255, 255, 255), thickness, lineType);
// Show support vectors
thickness = 2;
lineType = 8;
int c = SVM.get_support_vector_count();
for (int i = 0; i < c; ++i)
{
const float* v = SVM.get_support_vector(i);
circle(image, Point((int)v[0], (int)v[1]), 6, Scalar(128, 128, 128), thickness, lineType);
}
imwrite("result.png", image); // save the image
imshow("SVM Simple Example", image); // show it to the user
waitKey(0);
}
I'm new about opencv(c++) and kinect. I try to take a video image with c++ from kinect. I search everywhere but I didn't find anything. Because people are made using openNI or OpenKinect. I don't want to use this lib. How can I do it??
Thanks!!!
You could use the kinect for windows SDK to grab the frames, and then convert them to an opencv format. See this code example which does that in visual studio (found in this thread on the microsoft forums), unfortunately I don't have a kinect right now to test the code:
#include "stdafx.h"
#define COLOR_WIDTH 640
#define COLOR_HIGHT 480
#define DEPTH_WIDTH 320
#define DEPTH_HIGHT 240
#define SKELETON_WIDTH 640
#define SKELETON_HIGHT 480
#define CHANNEL 3
BYTE buf[DEPTH_WIDTH * DEPTH_HIGHT * CHANNEL];
int drawColor(HANDLE h, IplImage* color)
{
const NUI_IMAGE_FRAME * pImageFrame = NULL;
HRESULT hr = NuiImageStreamGetNextFrame(h, 0, &pImageFrame);
if (FAILED(hr))
{
cout << "Get Image Frame Failed" << endl;
return -1;
}
NuiImageBuffer * pTexture = pImageFrame->pFrameTexture;
KINECT_LOCKED_RECT LockedRect;
pTexture->LockRect(0, &LockedRect, NULL, 0);
if (LockedRect.Pitch != 0)
{
BYTE * pBuffer = (BYTE*) LockedRect.pBits;
cvSetData(color, pBuffer, LockedRect.Pitch);
}
cvShowImage("color image", color);
NuiImageStreamReleaseFrame(h, pImageFrame);
return 0;
}
int drawDepth(HANDLE h, IplImage* depth)
{
const NUI_IMAGE_FRAME * pImageFrame = NULL;
HRESULT hr = NuiImageStreamGetNextFrame(h, 0, &pImageFrame);
if (FAILED(hr))
{
cout << "Get Image Frame Failed" << endl;
return -1;
}
// temp1 = depth;
NuiImageBuffer * pTexture = pImageFrame->pFrameTexture;
KINECT_LOCKED_RECT LockedRect;
pTexture->LockRect(0, &LockedRect, NULL, 0);
if (LockedRect.Pitch != 0)
{
USHORT * pBuff = (USHORT*) LockedRect.pBits;
for (int i = 0; i < DEPTH_WIDTH * DEPTH_HIGHT; i++)
{
BYTE index = pBuff[i] & 0x07;
USHORT realDepth = (pBuff[i] & 0xFFF8) >> 3;
BYTE scale = 255 - (BYTE)(256 * realDepth / 0x0fff);
buf[CHANNEL * i] = buf[CHANNEL * i + 1] = buf[CHANNEL * i + 2] = 0;
switch (index)
{
case 0:
buf[CHANNEL * i] = scale / 2;
buf[CHANNEL * i + 1] = scale / 2;
buf[CHANNEL * i + 2] = scale / 2;
break;
case 1:
buf[CHANNEL * i] = scale;
break;
case 2:
buf[CHANNEL * i + 1] = scale;
break;
case 3:
buf[CHANNEL * i + 2] = scale;
break;
case 4:
buf[CHANNEL * i] = scale;
buf[CHANNEL * i + 1] = scale;
break;
case 5:
buf[CHANNEL * i] = scale;
buf[CHANNEL * i + 2] = scale;
break;
case 6:
buf[CHANNEL * i + 1] = scale;
buf[CHANNEL * i + 2] = scale;
break;
case 7:
buf[CHANNEL * i] = 255 - scale / 2;
buf[CHANNEL * i + 1] = 255 - scale / 2;
buf[CHANNEL * i + 2] = 255 - scale / 2;
break;
}
}
cvSetData(depth, buf, DEPTH_WIDTH * CHANNEL);
}
NuiImageStreamReleaseFrame(h, pImageFrame);
cvShowImage("depth image", depth);
return 0;
}
int drawSkeleton(IplImage* skeleton)
{
NUI_SKELETON_FRAME SkeletonFrame;
CvPoint pt[20];
HRESULT hr = NuiSkeletonGetNextFrame(0, &SkeletonFrame);
bool bFoundSkeleton = false;
for (int i = 0; i < NUI_SKELETON_COUNT; i++)
{
if (SkeletonFrame.SkeletonData[i].eTrackingState
== NUI_SKELETON_TRACKED)
{
bFoundSkeleton = true;
}
}
// Has skeletons!
//
if (bFoundSkeleton)
{
NuiTransformSmooth(&SkeletonFrame, NULL);
memset(skeleton->imageData, 0, skeleton->imageSize);
for (int i = 0; i < NUI_SKELETON_COUNT; i++)
{
if (SkeletonFrame.SkeletonData[i].eTrackingState
== NUI_SKELETON_TRACKED)
{
for (int j = 0; j < NUI_SKELETON_POSITION_COUNT; j++)
{
float fx, fy;
NuiTransformSkeletonToDepthImageF(
SkeletonFrame.SkeletonData[i].SkeletonPositions[j],
&fx, &fy);
pt[j].x = (int) (fx * SKELETON_WIDTH + 0.5f);
pt[j].y = (int) (fy * SKELETON_HIGHT + 0.5f);
cvCircle(skeleton, pt[j], 5, CV_RGB(255, 0, 0), -1);
}
cvLine(skeleton, pt[NUI_SKELETON_POSITION_HEAD],
pt[NUI_SKELETON_POSITION_SHOULDER_CENTER],
CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_SHOULDER_CENTER],
pt[NUI_SKELETON_POSITION_SPINE], CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_SPINE],
pt[NUI_SKELETON_POSITION_HIP_CENTER],
CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_HAND_RIGHT],
pt[NUI_SKELETON_POSITION_WRIST_RIGHT],
CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_WRIST_RIGHT],
pt[NUI_SKELETON_POSITION_ELBOW_RIGHT],
CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_ELBOW_RIGHT],
pt[NUI_SKELETON_POSITION_SHOULDER_RIGHT],
CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_SHOULDER_RIGHT],
pt[NUI_SKELETON_POSITION_SHOULDER_CENTER],
CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_SHOULDER_CENTER],
pt[NUI_SKELETON_POSITION_SHOULDER_LEFT],
CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_SHOULDER_LEFT],
pt[NUI_SKELETON_POSITION_ELBOW_LEFT],
CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_ELBOW_LEFT],
pt[NUI_SKELETON_POSITION_WRIST_LEFT],
CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_WRIST_LEFT],
pt[NUI_SKELETON_POSITION_HAND_LEFT], CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_HIP_CENTER],
pt[NUI_SKELETON_POSITION_HIP_RIGHT], CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_HIP_RIGHT],
pt[NUI_SKELETON_POSITION_KNEE_RIGHT],
CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_KNEE_RIGHT],
pt[NUI_SKELETON_POSITION_ANKLE_RIGHT],
CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_ANKLE_RIGHT],
pt[NUI_SKELETON_POSITION_FOOT_RIGHT],
CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_HIP_CENTER],
pt[NUI_SKELETON_POSITION_HIP_LEFT], CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_HIP_LEFT],
pt[NUI_SKELETON_POSITION_KNEE_LEFT], CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_KNEE_LEFT],
pt[NUI_SKELETON_POSITION_ANKLE_LEFT],
CV_RGB(0, 255, 0));
cvLine(skeleton, pt[NUI_SKELETON_POSITION_ANKLE_LEFT],
pt[NUI_SKELETON_POSITION_FOOT_LEFT], CV_RGB(0, 255, 0));
}
}
}
cvShowImage("skeleton image", skeleton);
return 0;
}
int main(int argc, char * argv[])
{
IplImage* color = cvCreateImageHeader(cvSize(COLOR_WIDTH, COLOR_HIGHT), IPL_DEPTH_8U, 4);
IplImage* depth = cvCreateImageHeader(cvSize(DEPTH_WIDTH, DEPTH_HIGHT),IPL_DEPTH_8U, CHANNEL);
IplImage* skeleton = cvCreateImage(cvSize(SKELETON_WIDTH, SKELETON_HIGHT),IPL_DEPTH_8U, CHANNEL);
cvNamedWindow("color image", CV_WINDOW_AUTOSIZE);
cvNamedWindow("depth image", CV_WINDOW_AUTOSIZE);
cvNamedWindow("skeleton image", CV_WINDOW_AUTOSIZE);
HRESULT hr = NuiInitialize(
NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX
| NUI_INITIALIZE_FLAG_USES_COLOR
| NUI_INITIALIZE_FLAG_USES_SKELETON);
if (hr != S_OK)
{
cout << "NuiInitialize failed" << endl;
return hr;
}
HANDLE h1 = CreateEvent(NULL, TRUE, FALSE, NULL);
HANDLE h2 = NULL;
hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR, NUI_IMAGE_RESOLUTION_640x480,
0, 2, h1, &h2);
if (FAILED(hr))
{
cout << "Could not open image stream video" << endl;
return hr;
}
HANDLE h3 = CreateEvent(NULL, TRUE, FALSE, NULL);
HANDLE h4 = NULL;
hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX,
NUI_IMAGE_RESOLUTION_320x240, 0, 2, h3, &h4);
if (FAILED(hr))
{
cout << "Could not open depth stream video" << endl;
return hr;
}
HANDLE h5 = CreateEvent(NULL, TRUE, FALSE, NULL);
hr = NuiSkeletonTrackingEnable(h5, 0);
if (FAILED(hr))
{
cout << "Could not open skeleton stream video" << endl;
return hr;
}
while (1)
{
WaitForSingleObject(h1, INFINITE);
drawColor(h2, color);
WaitForSingleObject(h3, INFINITE);
drawDepth(h4, depth);
WaitForSingleObject(h5, INFINITE);
drawSkeleton(skeleton);
//exit
int c = cvWaitKey(1);
if (c == 27 || c == 'q' || c == 'Q')
break;
}
cvReleaseImageHeader(&depth);
cvReleaseImageHeader(&color);
cvReleaseImage(&skeleton);
cvDestroyWindow("depth image");
cvDestroyWindow("color image");
cvDestroyWindow("skeleton image");
NuiShutdown();
return 0;
}
OpenCV does not offer the ability to connect to and process data from the Kinect sensor; unless you treat the Kinect as a regular webcam. You will want to fetch the data using one of the APIs and send it to OpenCV. To get the data from the Kinect you can use:
Microsoft Kinect for Windows SDK
OpenKinect's libfreenect API
OpenNI + OpenKinect
If your employer has a problem with one of the APIs, that is there choice. But the use of OpenCV does not eliminate your need to use one of them.
A quick search on MSDN reveals multiple threads on the the subject. The most straight forward approach I've read about is using cvSetData to import the data, after converting the image:
RGB
IplImage * ovImage = NULL;
ovImage = cvCreateImage(cvSize(640, 480), 8, 4);
cvSetData(ovImage, pBuffer, ovImage->widthStep);
Depth
ovImage = cvCreateImage(cvSize(640, 480), 8, 1);
I also found the freenomad_vision project on GitHub that provides libfreenect support with OpenCV and OpenGL. If you dislike using libfreenect, the code can easily serve as reference since the incoming data is all the same and (likely) would be converted the same.
In case if someone is redirected here looking for a simpler method for visualizing the Kinect depth stream, I was able to do this in the following way
for the KinectV2.
Mat CDepthMap::getFrame()
{
IDepthFrame* frame;
Mat depthImage;
hr = _depth_reader->AcquireLatestFrame(&frame);
if (SUCCEEDED(hr)) {
const UINT imgSize = sDepthWidth*sDepthHeight; //512*424
UINT16 pixelData[imgSize];
hr = frame->CopyFrameDataToArray(imgSize, pixelData);
if (SUCCEEDED(hr)) {
depthImage = Mat(sDepthHeight,sDepthWidth, CV_8U);
for (UINT i = 0; i < imgSize; i++) {
UINT16 depth = pixelData[i];
depthImage.at<UINT8>(i) = LOWORD(depth);
}
}
SafeRelease(frame);
}
return depthImage;
}