有趣的鼠标位置规律及其分析与实现
2010-03-01 00:57
537 查看
&
如果把一天下来鼠标的位置画成一张热度图,结果会怎么样?上面这张图解答了这个问题。虽然这一次用的数据不是特别多,只有约3000次采样(5秒一次),但还是可以看出来一些东西的。当然,如果数据更多的话效果应该会更好。
其中很明显的一个特征就是鼠标在右上角的概率特别高。这个很容易理解,经常去点那个小叉叉造成的嘛。但是为什么屏幕右边会有一列绿色呢?思考了一下,发现这可能因为我是用右手操作鼠标的。自己实际操作一下可以发现,如果是用右手握鼠标的话,光标很容易向右移过掉,直到在右侧屏幕边缘被挡住。这样光标就在相对较长的时间内滞留在屏幕右边。但同样的现象很难在左侧发生。因此仅在屏幕右边出现这样的密集区域。这个发现挺有意思的,似乎可以对HCI设计做出一些指导。但不晓得这样的分析是否有道理,希望大家指正。
此外,屏幕下方的一条显然是任务栏所致。左下角没有什么记录是因为我喜欢用Win键而不是用手点开始菜单。任务栏上只有一个地方是黄色,经过对照发现这里是任务管理器的位置所在。由于我个人习惯保持一个任务管理器的窗口并经常点开看看,记录图上才有此反映。
看来这个方法还可以观察到用户的一些小习惯呢。所以就像上篇文章说的,数据就是价值,而我们往往错过了太多的数据…
下面的部分是介绍这个小实验的实现。
总的来说思路很简单,首先记录鼠标位置,然后将其画成一个热度图即可。
关于获取鼠标位置,Windows有一个API: GetCursorPos()可以做这个事情。所以写一个简单的C++程序即可完成这个任务:
我们可以用PowerShell建一个后台任务,配合管道来保持时刻监控鼠标:
关于绘制热度图,由于我没有找到现成的库或者软件,MATLAB似乎可以,但还没有装好而且似乎效果比较粗糙,所以用C# + OpenCV自己写了一个。算法也不是很复杂,一共只有3步。第一步统计各个点的鼠标停留次数,第二步为了图形的自然美观做了一次大半径的高斯模糊,第三部将黑白的图像进行着色,变成一个彩色的热度图。OpenCV部分用的是EmguCV这一.NET封装。C#代码如下,供参考。
最后感谢Stephenjy同学提供灵感!
如果把一天下来鼠标的位置画成一张热度图,结果会怎么样?上面这张图解答了这个问题。虽然这一次用的数据不是特别多,只有约3000次采样(5秒一次),但还是可以看出来一些东西的。当然,如果数据更多的话效果应该会更好。
其中很明显的一个特征就是鼠标在右上角的概率特别高。这个很容易理解,经常去点那个小叉叉造成的嘛。但是为什么屏幕右边会有一列绿色呢?思考了一下,发现这可能因为我是用右手操作鼠标的。自己实际操作一下可以发现,如果是用右手握鼠标的话,光标很容易向右移过掉,直到在右侧屏幕边缘被挡住。这样光标就在相对较长的时间内滞留在屏幕右边。但同样的现象很难在左侧发生。因此仅在屏幕右边出现这样的密集区域。这个发现挺有意思的,似乎可以对HCI设计做出一些指导。但不晓得这样的分析是否有道理,希望大家指正。
此外,屏幕下方的一条显然是任务栏所致。左下角没有什么记录是因为我喜欢用Win键而不是用手点开始菜单。任务栏上只有一个地方是黄色,经过对照发现这里是任务管理器的位置所在。由于我个人习惯保持一个任务管理器的窗口并经常点开看看,记录图上才有此反映。
看来这个方法还可以观察到用户的一些小习惯呢。所以就像上篇文章说的,数据就是价值,而我们往往错过了太多的数据…
下面的部分是介绍这个小实验的实现。
总的来说思路很简单,首先记录鼠标位置,然后将其画成一个热度图即可。
关于获取鼠标位置,Windows有一个API: GetCursorPos()可以做这个事情。所以写一个简单的C++程序即可完成这个任务:
POINT point; do { GetCursorPos(&point); cout << "(gt;" << point.x << "," << point.y << ")" << endl; Sleep(5000); }while(true);
我们可以用PowerShell建一个后台任务,配合管道来保持时刻监控鼠标:
Start-job {GetMousePos.exe > Mouse.txt}
关于绘制热度图,由于我没有找到现成的库或者软件,MATLAB似乎可以,但还没有装好而且似乎效果比较粗糙,所以用C# + OpenCV自己写了一个。算法也不是很复杂,一共只有3步。第一步统计各个点的鼠标停留次数,第二步为了图形的自然美观做了一次大半径的高斯模糊,第三部将黑白的图像进行着色,变成一个彩色的热度图。OpenCV部分用的是EmguCV这一.NET封装。C#代码如下,供参考。
static void Main(string[] args) { //parse parameters if (args.Length != 1 && args.Length != 2) { Console.WriteLine("Parameter error!\r\nUsage:Create heat diagram from data of input file.\r\nHeatVisualizer <InputFilePath> [OutputImageFilePath]"); } var pointCount = new Dictionary<Point, int>(); Console.WriteLine("Please input data in (x, y) format."); var pointStrings = File.ReadAllLines(args[0]); foreach (var pointString in pointStrings) { var match = Regex.Match(pointString, @"\((\d+),\s*(\d+)\)"); if (match.Success) { var point = new Point(int.Parse(match.Groups[1].Value) / 3, int.Parse(match.Groups[2].Value) / 3); if (pointCount.ContainsKey(point)) { pointCount[point]++; } else { pointCount.Add(point, 1); } } else { Console.WriteLine("Error format!"); return; } } //smooth with OpenCV const int screenWidth = 1280 / 3 + 1, screenHeight = 1024 / 3 + 1; int maxCount = pointCount.Max(x => x.Value) / 10; var image = new Image<Gray, byte>(screenWidth, screenHeight); foreach (var keyValuePair in pointCount) { image[keyValuePair.Key.Y, keyValuePair.Key.X] = new Gray((double)keyValuePair.Value * 0xff / maxCount); } var image2 = new Image<Gray, byte>(screenWidth, screenHeight); CvInvoke.cvSmooth(image.Ptr, image2.Ptr, Emgu.CV.CvEnum.SMOOTH_TYPE.CV_GAUSSIAN, 25, 25, 0, 0); CvInvoke.cvEqualizeHist(image2.Ptr, image.Ptr); //visualize //calculate threshold int yellowCount = (int)(maxCount * 0.6 + 0.5); int yellowLength = maxCount - yellowCount; int greenCount = yellowCount / 2; int greenLength = yellowCount - greenCount; int whiteLength = greenCount; //colorize var resultImage = new Image<Bgr, byte>(screenWidth, screenHeight); for (int x = 0; x < image2.Width; x++) { for (int y = 0; y < image2.Height; y++) { int color = (int)image2[y, x].Intensity; if (color >= yellowCount) { int green = (int)(0xff - (color - yellowCount) * 0xff / yellowLength); resultImage[y, x] = new Bgr(0, green, 0xff); } else if (color >= greenCount) { int red = (int)((color - greenCount) * 0xff / greenLength); resultImage[y, x] = new Bgr(0, 0xff, red); } else { int red = (int)(0xff - (color) * 0xff / whiteLength); resultImage[y, x] = new Bgr(red, 0xff, red); } } } //write to file ImageViewer.Show(resultImage.Clone(), "ResultImage"); if (args.Length == 2) { resultImage.Save(args[1]); } //data sweep resultImage.Dispose(); image.Dispose(); image2.Dispose(); }
最后感谢Stephenjy同学提供灵感!
相关文章推荐
- firefox中用javascript实现鼠标位置的定位
- Native Hibernate与Hibernate JPA实现的区别及其分析
- 单例的五种实现方式,及其性能分析。
- 实现在Picture控件中获取鼠标位置(OnMouseMove)
- Java线程池及其底层源码实现分析
- concurrent包分析-阻塞队列BlockingQueue及其实现类
- 一个小语言的词法分析程序原理及其实现(2)
- Javascript中的DOM实现显示鼠标的空间位置
- 【算法分析】如何理解快慢指针?判断linked list中是否有环、找到环的起始节点位置。以Leetcode 141. Linked List Cycle, 142. Linked List Cycle II 为例Python实现
- 实现鼠标进入进出div时位置的监听效果
- 艾伟_转载:在WPF里面实现以鼠标位置为中心缩放移动图片
- jquery实现的元素的left增加N像素 鼠标移开会慢慢的移动到原来的位置
- “ACD及其实现方法”分析及学习
- 使用JDBC连接MySQL数据库--典型案例分析(九)----财务帐号的DAO设计及其实现
- firefox中用javascript实现鼠标位置的定位
- Java 并发包中的读写锁及其实现分析
- jQuery实现获取table中鼠标click点击位置行号与列号的方法
- 杨辉三角(Pascal Triangle)的几种C语言实现及其复杂度分析