90年代经典“手游”—拼图板小游戏Opencv实现
80后可能还对儿时玩过的一种经典木质的拼图板游戏记忆犹新,一般是一种4*4或5*5规格的手持活动板,通过挪动每个小板子的位置,拼出来板子上完整的图像,那时候还没有网吧,手机也还是大哥大的天下,所以这也可以算得上是最早的“手游”了吧……
简单的就是经典的,现在的Windows 7小工具里还保留了这个小游戏,当然你可能从来没有留意过~,就是下边的这个:
可以在控制面板->外观->桌面小工具里调出来。
这里准备用opencv里的模板匹配,通过鼠标响应事件来实现这个小游戏。
首先第一步是对图像按照传入的行列参数分割,并把分割出来的行*列个个数的子图像在另一空白图像中显示出来:
for(int i=0;i<rows;i++)
	{
		for(int j=0;j<cols;j++)
		{
			Mat SourceRoi=Sourceimage(Rect(j*Roicols,i*Roirows,Roicols-1,Roirows-1));
			arraryimage.push_back(SourceRoi);
		}
	}
rows和cols分别是用户定义的行列数,arraryimage是定义的 vector<Mat>类型的向量。
分割完之后需要把这些子图像随机的显示在另一空白图像中,这里写了一个生成指定区间里的不重复的随机数来实现:
//*******************************************************************//
//随机调换所有的子图像序列的位置,用于在 Splite image中显示
//*******************************************************************//
void Randarrary( vector<Mat>& vectorMat)
{
	for(int i=0;i<vectorMat.size();i++)
	{
		srand(int(time(0)));
		int a=rand()%(vectorMat.size()-i)+i;
		swap(vectorMat[i],vectorMat[a]);
	}
}
C++中使用rand()生成随机数记得先定义种子,不然系统会默认种子为1,这样每次生成的随机序列都是一样的,第一个随机数永远是41,关于rand()以后再说一说。
每生成一个随机数,就把该随机数作下标的向量元素跟第一个元素对换,实现生成不重复的随机数。
所有分割出来的子图像按随机顺序组成了“Splite image”图像后,通过鼠标单击事件响应函数,定位到鼠标单击点坐在的子图像,并把该子图像用模板匹配方法在原图像中定位出位置,最后合成到目标图像“Jigsaw image”
//*******************************************************************//
//鼠标回调函数,用于获取需要查找的子图像在原图像中的位置,并在叠加显示在目标图像中
//*******************************************************************//
void OnMouseAction(int event,int x,int y,int flags,void *ustc)
{
	if(event==CV_EVENT_LBUTTONDOWN)
	{
		Mat RoiSpilte,RoiSource;
		int rows=(y/Roirows)*Roirows;
		int clos=(x/Roicols)*Roicols;
		RoiSpilte=Spilteimage(Rect(clos,rows,Roicols,Roirows));
		imshow("Slice",RoiSpilte);
		Mat image=Mat::zeros(Sourceimage.rows-Roirows,Sourceimage.cols-Roicols,CV_32FC1);
		matchTemplate(Sourceimage,RoiSpilte,image,1);
		normalize(image,image,0,1,NORM_MINMAX);
		double minV=0;
		double maxV=0;
		Point minP,maxP;
		minMaxLoc(image,&minV,&maxV,&minP,&maxP);
		//Mat ROIS=Sourceimage(Rect(maxP.x,maxP.y,Roicols,Roirows));
		Mat ROIDst=Dstimage(Rect(minP.x,minP.y,Roicols,Roirows));
		addWeighted(ROIDst,0,RoiSpilte,1,0,ROIDst,-1);
		imshow("Jigsaw image",Dstimage);
	}
原图像:
行列分割后的图像,子图像位置随机分布:
单击Splite image图像中的子图像,叠加该子图像到目标图像上,子图像位置通过模板匹配方法在原图像中定位:
完成后效果:
为了清楚显示边界,每个子图像在行列上都减了一个像素,所以上图可见黑色线条。
完整程序:
#include "core/core.hpp"
#include "highgui/highgui.hpp"
#include "imgproc/imgproc.hpp"
#include <time.h>
using namespace cv;
Mat Sourceimage,Spilteimage,Rebuildimage,Dstimage;
int rows,cols;
int Roirows,Roicols;
vector<Mat>arraryimage;
void Randarrary( vector<Mat> &vectorMat);    //随机排列子图像序列函数
static int vectornumber=0;
void OnMouseAction(int event,int x,int y,int flags,void *ustc);  //鼠标回调事件函数
int main(int argc,char*argv[])
{
	Sourceimage=imread(argv[1]);
	imshow("Source image",Sourceimage);
	rows=atoi(argv[2]);
	cols=atoi(argv[3]);
	Roirows=Sourceimage.rows/rows;
	Roicols=Sourceimage.cols/cols;
	Spilteimage=Mat::zeros(Sourceimage.rows,Sourceimage.cols,Sourceimage.type());
	Dstimage=Mat::zeros(Sourceimage.rows,Sourceimage.cols,Sourceimage.type());
	for(int i=0;i<rows;i++)
	{
		for(int j=0;j<cols;j++)
		{
			Mat SourceRoi=Sourceimage(Rect(j*Roicols,i*Roirows,Roicols-1,Roirows-1));
			arraryimage.push_back(SourceRoi);
		}
	}
	// 随机函数
	Randarrary( arraryimage);
	for(int i=0;i<rows;i++)
	{
		for(int j=0;j<cols;j++)
		{
			Mat SpilterRoi=Spilteimage(Rect(j*Roicols,i*Roirows,Roicols-1,Roirows-1));
			addWeighted(SpilterRoi,0,arraryimage[vectornumber],1,0,SpilterRoi);
			vectornumber++;
			imshow("Splite image",Spilteimage);
			waitKey(150);
		}
	}
	setMouseCallback("Splite image",OnMouseAction);
	waitKey();
}
//*******************************************************************//
//随机调换所有的子图像序列的位置,用于在 Splite image中显示
//*******************************************************************//
void Randarrary( vector<Mat>& vectorMat)
{
	for(int i=0;i<vectorMat.size();i++)
	{
		srand(int(time(0)));
		int a=rand()%(vectorMat.size()-i)+i;
		swap(vectorMat[i],vectorMat[a]);
	}
}
//*******************************************************************//
//鼠标回调函数,用于获取需要查找的子图像在原图像中的位置,并在叠加显示在目标图像中
//*******************************************************************//
void OnMouseAction(int event,int x,int y,int flags,void *ustc)
{
	if(event==CV_EVENT_LBUTTONDOWN)
	{
		Mat RoiSpilte,RoiSource;
		int rows=(y/Roirows)*Roirows;
		int clos=(x/Roicols)*Roicols;
		RoiSpilte=Spilteimage(Rect(clos,rows,Roicols,Roirows));
		imshow("Slice",RoiSpilte);
		Mat image=Mat::zeros(Sourceimage.rows-Roirows,Sourceimage.cols-Roicols,CV_32FC1);
		matchTemplate(Sourceimage,RoiSpilte,image,1);
		normalize(image,image,0,1,NORM_MINMAX);
		double minV=0;
		double maxV=0;
		Point minP,maxP;
		minMaxLoc(image,&minV,&maxV,&minP,&maxP);
		Mat ROIDst=Dstimage(Rect(minP.x,minP.y,Roicols,Roirows));
		addWeighted(ROIDst,0,RoiSpilte,1,0,ROIDst,-1);
		imshow("Jigsaw image",Dstimage);
	}
}
资源文件和Code也可以在点击这里 拼图板小游戏 免积分下载
90年代经典“手游”—拼图板小游戏Opencv实现的更多相关文章
- .NET手撸2048小游戏
		
.NET手撸2048小游戏 2048是一款益智小游戏,得益于其规则简单,又和2的倍数有关,因此广为人知,特别是广受程序员的喜爱. 本文将再次使用我自制的"准游戏引擎"FlysEng ...
 - Unity手游之路游戏摇杆之Easy Touch 3教程
		
之前已经介绍过Unity自带的摇杆Joystick,它用起来很简单.但是它也存在很多局限,不能全部满足普通mmo手游的一些需求,例如:要能方便地更好素材:能指定在某个区域显示,或者只有在该区域触摸时才 ...
 - 【cocos2d-x 手游研发小技巧(6)聊天系统+字体高亮】
		
转载请注明出处:http://www.cnblogs.com/zisou/p/cocos2dxJQ-6.html 聊天系统在手机网游中是最常见的交互工具,大家在一起边玩游戏边聊天岂不乐哉: 废话不多了 ...
 - Unity手撸2048小游戏——模块拆分
		
最近惹女票生气了,想起撸个游戏来哄哄她,加之以前在小恩爱App上,玩过那情侣版的2048,加之她喜欢玩这类益智类的游戏,打算撸一个3D的情侣版2048.不过之前没怎么独立做过游戏,就从2D的开始吧. ...
 - 【cocos2d-x 手游研发小技巧(8)通讯的数据压缩与解压 】
		
今天说一下手机游戏通讯协议中的数据问题,大量的数据将给服务器端和客户端带来很大的压力,一般来说. 转载请注明出处:http://www.cnblogs.com/zisou/p/cocos2dxJQ-8 ...
 - Unity手撸2048小游戏——自动生成4*4棋盘
		
1.新建文件夹,命prefabs,将刚刚做成的Chessman拖入该文件下,做成预制体 2.删除panel下的Chessman 3.在panel下,新建一个空对象,命名为Chessboard,大小设置 ...
 - 【cocos2d-x 手游研发小技巧(7)图片资源加密,Lua文件加密】
		
游戏开发中常遇到资源保护的问题. 目前游戏开发中常加密的文件类型有:图片,Lua文件,音频等文件,而其实加密也是一把双刃剑. 需要安全那就得耗费一定的资源去实现它.目前网上也有用TexturePack ...
 - 【cocos2d-x 手游研发小技巧(4)与Android混编实现换“头像图片”】
		
cocos2dx在android平台上的游戏开发中往往会遇到一些混编需求,如: 比方有的社区类游戏需要用到更换玩家的“头像”操作,其实就是调用android servers服务里面的本地图片,以及选取 ...
 - 【cocos2d-x 手游研发小技巧(3)Android界面分辨率适配方案】
		
先感叹一下吧~~android的各种分辨率各种适配虐我千百遍,每次新项目我依旧待它如初恋···· 每家公司都有自己项目工程适配的方案,这种东西就是没有最好,只有最适合!!! 这次新项目专项针对andr ...
 
随机推荐
- linux的crontab定时任务命令
			
linux的crontab定时任务命令 cron是一个linux下的定时执行工具. 启动.停止.重启.重新载入配置/sbin/service crond start|stop|restart|relo ...
 - Nginx反向代理新篇-使用location对多个URL做反向代理
			
1.原理 Nginx解析location/后面的字符串,配置不同的字符串匹配不同的URL进行反向代理. 2.nginx.conf配置文件 worker_processes 1; events { wo ...
 - Codeforces 961 E Tufurama
			
Discription One day Polycarp decided to rewatch his absolute favourite episode of well-known TV seri ...
 - iOS 内存管理实践
			
内存管理实践 尽管基本的概念在内存管理策略文章中简单得阐述了,但是还有一些实用的步骤让你更容易管理内存:有助于确保你的程序最大限度地减少资源需求的同时,保持可靠和强大. 使用“访问器方法”让内存管理更 ...
 - Go -- log4go日志
			
折腾: [已解决]go语言中实现log信息同时输出到文件和控制台(命令行) 期间,已经通过io的MultiWriter搞定了同时输出信息到文件和console,但是不支持level. 所以,再去试试这 ...
 - XStream 数组(List)输出结构
			
<!-- 期望的DOM树 --> <Articles> <item> <Title>微信SDK初步结构</Title> <Descri ...
 - C++ 学习总结 复习篇
			
友元的使用 分为友元类和友元函数 //友元类与友元函数的共同点:都可以让某一个类作为另一个类或者函数的参数.   //友元类:它让当前类成为另一个类的友元,然后,另一个类 ...
 - 史上最全的CSS hack方式一览  jQuery 图片轮播的代码分离  JQuery中的动画  C#中Trim()、TrimStart()、TrimEnd()的用法  marquee 标签的使用详情  js鼠标事件  js添加遮罩层 页面上通过地址栏传值时出现乱码的两种解决方法  ref和out的区别在c#中 总结
			
史上最全的CSS hack方式一览 2013年09月28日 15:57:08 阅读数:175473 做前端多年,虽然不是经常需要hack,但是我们经常会遇到各浏览器表现不一致的情况.基于此,某些情况我 ...
 - Elasticsearch 学习笔记 Elasticsearch及Elasticsearch head安装配置
			
一.安装与配置 1.到官网下载Elasticsearch,https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6. ...
 - u-boot简单学习笔记(二)——AR9331 uboot.lds分析
			
最开始系统上电后 从uboot.lds开始引导 OUTPUT_FORMAT("elf32-tradbigmips", "elf32-tradbigmips", ...