接着上次代码,这次实验完成平移动作。
由于之前已经写好了OpenCV对于图片的平移和缩放两个函数,这次直接拿过来用即可
上一个代码中缩放用的手势特征是空间球半径,这次改用为手势的高度。
首先来看看Leap Motion的坐标系:
这次用到的接口函数是:hand.palmPosition().x;hand.palmPosition().y;hand.palmPosition().z;通过这三个距离便可以进行图片的相应移动缩放了。
话不多说,上代码:
#include <iostream>
#include <cstring>
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <Leap.h>
using namespace Leap;
using namespace cv;
using namespace std;
bool flags=1;
float radios;
float position[3];
class MouseListener : public Listener {
public:
Mat image, image_size_change, image_for_display;//image是原始图片,永远不做更改。image_size_change相当于每一次更改后的图片。image_for_display是用来显示的,相当于包含了image_size_change的画布。
float mul = 1.0;
void imshow_FullScreen(Mat img);//在画布上显示图片
void Translation(int dx, int dy);//图片平移
void Change_Size(float div);//图片放大缩小
MouseListener(int width, int length, int x, int y)
{
Screen_Width = width;
Screen_Length = length;
point_O.x = x;
point_O.y = y;
}
MouseListener()
{
Screen_Width = 1080;
Screen_Length = 1920;
point_O.x = 0;
point_O.y = 0;
}
virtual void onInit(const Controller&);
virtual void onConnect(const Controller&);
virtual void onFrame(const Controller&);
virtual void onDisconnect(const Controller&);
virtual void onExit(const Controller&);
/*virtual void onFocusGained(const Controller&);
virtual void onFocusLost(const Controller&);
virtual void onDeviceChange(const Controller&);*/
virtual void onServiceConnect(const Controller&);
virtual void onServiceDisconnect(const Controller&);
/*virtual void onServiceChange(const Controller&);
virtual void onDeviceFailure(const Controller&);
virtual void onLogMessage(const Controller&, MessageSeverity severity, int64_t timestamp, const char* msg);
*/
private:
Point point_O;//图片的左上方原点(用来定位图片)
int Screen_Width, Screen_Length;//画布的长和宽
};
void MouseListener::onInit(const Controller& controller)
{
std::cout << "初始化完成" << std::endl;
}
void MouseListener::onConnect(const Controller& controller)
{
std::cout << "Connected" << std::endl;
/*controller.enableGesture(Gesture::TYPE_CIRCLE);//解锁手势——画圈
controller.enableGesture(Gesture::TYPE_KEY_TAP);//解锁手势——点击
controller.enableGesture(Gesture::TYPE_SWIPE);//解锁手势——挥手
controller.enableGesture(Gesture::TYPE_SCREEN_TAP);//解锁手势——点击屏幕
controller.setPolicyFlags(Controller::PolicyFlag::POLICY_BACKGROUND_FRAMES);*///允许程序在后台时依然可以接收到Leap Motion的消息
}
void MouseListener::onDisconnect(const Controller& controller)
{
std::cout << "Disconnected" << std::endl;
}
void MouseListener::onExit(const Controller& controller)
{
std::cout << "Exited" << std::endl;
}
void MouseListener::onServiceConnect(const Controller& controller)
{
std::cout << "ServiceConnected" << std::endl;
}
void MouseListener::onServiceDisconnect(const Controller& controller)
{
std::cout << "ServiceDisconnected" << std::endl;
}
void MouseListener::onFrame(const Controller& controller)
{
const Frame frame = controller.frame();//定义一个帧
ImageList images = frame.images();//定义一个帧的采集到的图像
HandList hands = frame.hands();
const Hand hand = *hands.begin();
position[0] = hand.palmPosition().x;
position[1] = hand.palmPosition().y;
position[2] = hand.palmPosition().z;
flags=0;
radios = hand.sphereRadius();//用来确保有手进入设备视野
cout << position[0] <<" ; "<< position[1] << " ; " << position[2] << ::endl;//输出相应原始距离便于调试
}
/****************带有平移操作的图片显示函数,自定义画布*****************/
void MouseListener::Translation(int dx, int dy)
{
point_O.x += dx;
point_O.y += dy;
imshow_FullScreen(image_size_change);
}
/****************显示带有画布的图片*****************/
void MouseListener::imshow_FullScreen(Mat img)
{
copyMakeBorder(img, image_for_display, point_O.y, Screen_Width - point_O.y - img.rows, point_O.x, Screen_Length - point_O.x - img.cols, BORDER_CONSTANT, Scalar(190, 190, 190));//OpenCV自带的扩充图片的函数
namedWindow("Canvases", WINDOW_AUTOSIZE);
circle(image_for_display, point_O, 5, Scalar(0, 0, 255));//标注原点位置方便调试
imshow("Canvases", image_for_display);//最终显示的是扩充后的图片
}
/****************带有缩放操作的图片显示函数,自定义画布*****************/
void MouseListener::Change_Size(float div)
{
point_O.x += (image_size_change.cols - image_size_change.cols*div) / 2;//计算缩放后的新的坐标
point_O.y += (image_size_change.rows - image_size_change.rows*div) / 2;
mul *= div;//采用对每一次的缩放因子累计相乘的方式来消除因为缩放引起的模糊问题
resize(image, image_size_change, Size(), mul, mul);//这里使用的是image原始图片,因此不会越来越模糊
imshow_FullScreen(image_size_change);
}
int main(int argc, char**argv)
{
Point point(0.0,0.0);
float div;
MouseListener listener;//定义一个监听类变量
Controller controller;//定义一个控制类变量
controller.addListener(listener);//将监听类变量加入控制之中
controller.setPolicy(Leap::Controller::POLICY_BACKGROUND_FRAMES);//打开frame功能
controller.setPolicy(Leap::Controller::POLICY_IMAGES);//打开images功能
MouseListener picture(1080, 1920, 500, 500);//定义一个1080*1920大小的画布,初始原点在(500,500)的位置
picture.image = imread("C:\\Users\\MacheNike\\Desktop\\lena.bmp");//读入原始图片
resize(picture.image, picture.image_size_change, Size(), 1, 1);//开始先让缓存图片与原始图片一致
picture.Translation(100, 60);
waitKey(500);
picture.Change_Size(0.4);
waitKey(500);
picture.Translation(0, -350);//通过平移与缩放,使图片初始位置在画布正中间
waitKey(500);
while (1)
{
if ((flags == 0)&&(radios!=0))//采集到一帧图像时进入此程序
{
flags = 1;
div = 1 + (position[1] - 125) / 60;//调节参数使缩放因子和手的高度之间的比例比较合适
point.x += position[0];
point.y += position[2];//多次实验发现,采用累加的方式比直接使用坐标的方式效果更好(有一种结合了加速度的感觉)
picture.Translation(point.x/14,point.y/14);//依然调节参数找到最佳效果
picture.Change_Size(div);
waitKey(1);
while (flags==1);//等待下一帧到来
/*************由于我的OpenCV函数利用的是变化量操作图片的,因此要么每次显示完毕后复位,要么计算出每两帧手势距离的变化量,后者相对复杂一点, 直接采用了复位的方法**************/
picture.Change_Size(1 / div);//大小初始化
picture.Translation((-1)*point.x / 14, (-1)*point.y / 14);//位置初始化
picture.Translation(1, 1);//补偿缩放交互时误差引起的图片位移
}
}
controller.removeListener(listener);//移除监听类
}
本次实验没有相应的动态图片,但是亲测大量次,效果比较不错,图片的稳定性比较高(手几乎不动的时候图片的波动情况)。之后可以尝试使用两帧手部距离之间的变化量来实现这个功能,这样就可以自己设定一个阈值从而使得图片稳定性大大提高了!
至此,我们已经基本上掌握了Leap Motion与二维图像的简单交互,接下来可以学习如何对图片进行旋转,再进入到Leap Motion与三维图像的交互。
整体思想和常用的接口函数大家已经可以说是应该学会了,大家可以尝试动手编写属于自己的代码,也欢迎和我这个小白多多交流,下期再见!!!