目录
第一个Azure Kinect应用程序:查找Azure Kinect DK设备
基于opencv实现实时RGB、Depth、Ir图像采集及本地保存
-
第一个Azure Kinect应用程序:查找Azure Kinect DK设备
安装Visual Studio并创建一个项目Kinect,然后右键项目找到“管理NuGet程序包”选项:
在浏览里面搜索“Microsoft.Azure.Kinect.Sensor”,安装一下:
添加头文件k4a.h,默认路径为“C:\Program Files\Azure Kinect SDK v1.4.1\sdk\include\k4a\k4a.h":
在源文件中,创建一个test.cpp:
将项目的属性改为x64:
添加头文件目录("C:\Program Files\Azure Kinect SDK v1.4.1\sdk\include"):
添加库文件目录("C:\Program Files\Azure Kinect SDK v1.4.1\sdk\windows-desktop\amd64\release\lib"):
添加库文件附加依赖项("C:\Program Files\Azure Kinect SDK v1.4.1\sdk\windows-desktop\amd64\release\lib\k4a.lib"):
添加完记得点右下角的应用,然后点击确定。
在test.cpp中运行以下代码:
#pragma comment(lib, "k4a.lib")
#include <k4a/k4a.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
uint32_t count = k4a_device_get_installed_count();
if (count == 0)
{
printf("No k4a devices attached!\n");
return 1;
}
// Open the first plugged in Kinect device
k4a_device_t device = NULL;
if (K4A_FAILED(k4a_device_open(K4A_DEVICE_DEFAULT, &device)))
{
printf("Failed to open k4a device!\n");
return 1;
}
// Get the size of the serial number
size_t serial_size = 0;
k4a_device_get_serialnum(device, NULL, &serial_size);
// Allocate memory for the serial, then acquire it
char* serial = (char*)(malloc(serial_size));
k4a_device_get_serialnum(device, serial, &serial_size);
printf("Opened device: %s\n", serial);
free(serial);
// Configure a stream of 4096x3072 BRGA color data at 15 frames per second
k4a_device_configuration_t config = K4A_DEVICE_CONFIG_INIT_DISABLE_ALL;
config.camera_fps = K4A_FRAMES_PER_SECOND_15;
config.color_format = K4A_IMAGE_FORMAT_COLOR_BGRA32;
config.color_resolution = K4A_COLOR_RESOLUTION_3072P;
// Start the camera with the given configuration
if (K4A_FAILED(k4a_device_start_cameras(device, &config)))
{
printf("Failed to start cameras!\n");
k4a_device_close(device);
return 1;
}
// Camera capture and application specific code would go here
// Shut down the camera when finished with application logic
k4a_device_stop_cameras(device);
k4a_device_close(device);
return 0;
}
正常运行结果如下:
-
基于opencv实现实时RGB、Depth、Ir图像采集及本地保存
opencv官网链接:Releases - OpenCV
解压:
配置系统环境变量:
将D:\opencv3.4.1\build\x64\vc14\bin文件夹下的opencv_ffmpeg341_64.dll、opencv_world341.dll、opencv_world341d.dll三个文件复制到C:\Windows\SysWOW64和C:\Windows\System32两个文件夹中:
在Kinect项目中,菜单栏->视图->其他窗口->属性管理器
在属性管理器中选择debugx64,右键选择属性:
将“D:\opencv3.4.1\build\include”、“D:\opencv3.4.1\build\include\opencv”和“D:\opencv3.4.1\build\include\opencv2”三条路径添加到包含目录中:
将“D:\opencv3.4.1\build\x64\vc14\lib”路径添加到库目录中:
将opencv_world341d.lib名字粘贴到debug和项目两者的链接器输入中的附加依赖项中:
测试,运行后不报错且正确显示图片即证明VS上的opencv配置全部成功:
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace cv;
int main()
{
//读入一张图片(选择自己图片的绝对路径)
Mat img = imread("C:/Users/Pictures/Saved Pictures/green spring.jpg");
// 创建一个名为‘test’的窗口
namedWindow("test");
imshow("test", img);
waitKey(0);
return 0;
}
在kinect项目中,创建saveimage.cpp,完整代码如下:
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include <iostream>
#include <fstream>
#include <chrono>
#include <thread>
#include <iomanip>
#include <k4a/k4a.hpp>
#include <opencv2/opencv.hpp>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
int main() {
// 初始化 Azure Kinect 设备
k4a::device device;
try {
device = k4a::device::open(K4A_DEVICE_DEFAULT);
}
catch (const std::exception& e) {
std::cerr << "打开 Kinect 设备失败: " << e.what() << std::endl;
return -1;
}
k4a_device_configuration_t config = K4A_DEVICE_CONFIG_INIT_DISABLE_ALL;
config.camera_fps = K4A_FRAMES_PER_SECOND_30;
config.color_format = K4A_IMAGE_FORMAT_COLOR_BGRA32;
/*
K4A_COLOR_RESOLUTION_OFF = 0, Color camera will be turned off with this setting
K4A_COLOR_RESOLUTION_720P, 1280 * 720 16:9
K4A_COLOR_RESOLUTION_1080P, 1920 * 1080 16:9
K4A_COLOR_RESOLUTION_1440P, 2560 * 1440 16:9
K4A_COLOR_RESOLUTION_1536P, 2048 * 1536 4:3
K4A_COLOR_RESOLUTION_2160P, 3840 * 2160 16:9
K4A_COLOR_RESOLUTION_3072P, 4096 * 3072 4:3
*/
config.color_resolution = K4A_COLOR_RESOLUTION_720P;
/*
Azure Kinect 深度相机支持不同的工作模式,主要区别在视场(FOV)大小和图像分辨率方面。这四种模式分别是:
NFOV_2X2BINNED (Narrow Field of View, 2x2 Binned):视场角较小,适合近距离捕获,景深范围较窄。
原始分辨率为 640x576,但输出时进行了 2x2 的 binning,即将相邻的 2x2 像素求平均合并为一个像素,最终输出分辨率为 320x288。
帧率可达 30 FPS。
NFOV_UNBINNED (Narrow Field of View, Unbinned):视场角与 NFOV_2X2BINNED 相同。
输出原始分辨率 640x576,没有进行 binning。
帧率同样可达 30 FPS,但每帧处理时间更长。
WFOV_2X2BINNED (Wide Field of View, 2x2 Binned):视场角更大,适合捕获更广阔的场景,但景深范围相对 NFOV 更有限。
原始分辨率为 1024x1024,经过 2x2 binning 后输出分辨率为 512x512。
帧率可达 30 FPS。
WFOV_UNBINNED (Wide Field of View, Unbinned):视场角与 WFOV_2X2BINNED 相同。
输出原始分辨率 1024x1024,没有进行 binning。
由于数据量大,帧率只有 15 FPS。
总的来说:NFOV 模式适合近距离精细捕获,WFOV 模式适合大范围低精度捕获。
Binning 通过降低分辨率来提高帧率和降低噪声,但细节会有损失。Unbinned 模式分辨率高,细节丰富,但帧率较低,噪声更多。
*/
config.depth_mode = K4A_DEPTH_MODE_NFOV_UNBINNED;
config.synchronized_images_only = true;
try {
device.start_cameras(&config);
}
catch (const std::exception& e) {
std::cerr << "启动相机失败: " << e.what() << std::endl;
device.close();
return -1;
}
std::cout << "相机启动成功。" << std::endl;
// 保存路径
std::string colorSavePath = "D:\\Azure Kinect Data\\rgb\\";
std::string colorTxtFilename = colorSavePath + "color_data.txt";
std::string depthSavePath = "D:\\Azure Kinect Data\\depth\\";
std::string depthTxtFilename = depthSavePath + "depth_data.txt";
std::string irSavePath = "D:\\Azure Kinect Data\\ir\\";
std::string irTxtFilename = irSavePath + "ir_data.txt";
// 如果文件夹不存在,则创建文件夹
if (!fs::exists(colorSavePath)) {
fs::create_directories(colorSavePath);
}
if (!fs::exists(depthSavePath)) {
fs::create_directories(depthSavePath);
}
if (!fs::exists(irSavePath)) {
fs::create_directories(irSavePath);
}
std::ofstream colorTxtFile(colorTxtFilename);
std::ofstream depthTxtFile(depthTxtFilename);
std::ofstream irTxtFile(irTxtFilename);
if (!colorTxtFile.is_open() || !depthTxtFile.is_open() || !irTxtFile.is_open()) {
std::cerr << "打开数据文件失败。" << std::endl;
device.close();
return -1;
}
bool isRecording = false;
bool stopRecording = false;
int frameCount = 0;
std::chrono::high_resolution_clock::time_point startTime;
// 处理用户输入的线程
std::thread inputThread([&]() {
while (!stopRecording) {
std::string command;
std::cout << "输入 'start' 开始录制,输入 'end' 停止录制: ";
std::getline(std::cin, command);
if (command == "start") {
startTime = std::chrono::high_resolution_clock::now();
isRecording = true;
std::cout << "开始录制..." << std::endl;
}
else if (command == "end") {
isRecording = false;
stopRecording = true;
std::cout << "停止录制。" << std::endl;
}
else {
std::cout << "无效的命令。请输入 'start' 或 'end'。" << std::endl;
}
}
});
// 捕获图像的主循环
k4a::capture capture;
while (!stopRecording) {
if (isRecording && device.get_capture(&capture)) {
auto currentTime = std::chrono::high_resolution_clock::now();
auto elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime);
double relativeTime = elapsedTime.count() / 1000.0;
// 捕获彩色图像
k4a::image colorImg = capture.get_color_image();
if (colorImg) {
uint8_t* colorBuffer = colorImg.get_buffer();
int colorWidth = colorImg.get_width_pixels();
int colorHeight = colorImg.get_height_pixels();
cv::Mat colorImage(colorHeight, colorWidth, CV_8UC4, colorBuffer, colorImg.get_stride_bytes());
cv::cvtColor(colorImage, colorImage, cv::COLOR_BGRA2BGR);
std::string colorFilename = "color_frame_" + std::to_string(frameCount) + ".png";
std::string colorFilePath = colorSavePath + colorFilename;
cv::imwrite(colorFilePath, colorImage);
// 保存彩色图像数据
colorTxtFile << "Color Image: " << colorFilename << ", Time: " << std::fixed << std::setprecision(3) << relativeTime << "s" << std::endl;
}
// 捕获深度图像
k4a::image depthImg = capture.get_depth_image();
if (depthImg) {
int depthWidth = depthImg.get_width_pixels();
int depthHeight = depthImg.get_height_pixels();
// 将深度图像对齐到彩色图像
k4a::calibration calibration = device.get_calibration(config.depth_mode, config.color_resolution);
k4a::transformation transform(calibration);
k4a::image alignedDepthImg = transform.depth_image_to_color_camera(depthImg);
uint16_t* alignedDepthBuffer = reinterpret_cast<uint16_t*>(alignedDepthImg.get_buffer());
cv::Mat alignedDepthImage(depthHeight, depthWidth, CV_16U, alignedDepthBuffer, alignedDepthImg.get_stride_bytes());
// 将对齐后的深度图像转换为8位深度图像
cv::Mat alignedDepthImage8U;
alignedDepthImage.convertTo(alignedDepthImage8U, CV_8U, 1.0 ); // 将16位深度图像缩放到8位
// 保存对齐后的8位深度图像
std::string depthFilename = "depth_frame_" + std::to_string(frameCount) + ".png";
std::string depthFilePath = depthSavePath + depthFilename;
cv::imwrite(depthFilePath, alignedDepthImage8U);
// 保存深度图像数据
depthTxtFile << "Depth Image: " << depthFilename << ", Time: " << std::fixed << std::setprecision(3) << relativeTime << "s" << std::endl;
}
// 捕获IR图像
k4a::image irImg = capture.get_ir_image();
if (irImg) {
int irWidth = irImg.get_width_pixels();
int irHeight = irImg.get_height_pixels();
uint16_t* irBuffer = reinterpret_cast<uint16_t*>(irImg.get_buffer());
cv::Mat irImage(irHeight, irWidth, CV_16U, irBuffer, irImg.get_stride_bytes());
// 将16位IR图像转换为8位灰度图像
cv::Mat irImage8U;
irImage.convertTo(irImage8U, CV_8U, 1.0 );
// 保存8位IR图像
std::string irFilename = "ir_frame_" + std::to_string(frameCount) + ".png";
std::string irFilePath = irSavePath + irFilename;
cv::imwrite(irFilePath, irImage8U);
// 保存IR图像数据
irTxtFile << "IR Image: " << irFilename << ", Time: " << std::fixed << std::setprecision(3) << relativeTime << "s" << std::endl;
}
frameCount++;
}
}
// 等待输入线程完成
inputThread.join();
// 关闭数据文件
colorTxtFile.close();
depthTxtFile.close();
irTxtFile.close();
// 停止相机并关闭设备
device.stop_cameras();
device.close();
return 0;
}
运行结果如下: