先放一个效果图,如下:
设计的主体思路:
1.图像的快速截取
2.图像的预处理
3.识别模型的设计和训练
4.图像匹配
5.业务逻辑的实现
其中,图像处理主要是基于安卓版opencv。识别模型是基于cnn神经网络。
难点
1.粘连图像的分割
2.鉴于设计的以通用为目的,所以不同游戏平台的扑克牌长得千差万别,记牌器本身的兼容性成为一个巨大的挑战。
3.去除特效干扰(炸弹的特效,飞机特效等)。
4.性能,在调用图像处理算法,神经网络算法的前提下,需要保证在普通的智能手机上占用很小的性能实现在毫秒级别的识别。
一 图像的快速截取
截屏方法有多种。我们的需求是连续,快速,少占资源,非root的截屏方式。目前符合这一条件的是安卓5.0以上版本(毕竟目前都8.0了,5.0以下的就自动忽略啦)提供的API。
Android 在5.0 之后支持了实时录屏的功能。通过实时录屏我们可以拿到截屏的图像。同时可以通过在Service中处理实现后台的录屏。
大体步骤:
1.初始化一个MediaProjectionManager。
MediaProjectionManager mMediaProjectionManager = (MediaProjectionManager)getApplication().getSystemService(Context.MEDIA_PROJECTION_SERVICE);
2.创建intent,并启动Intent。注意这里是startActivityForResult
startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
3.在onActivityResult中拿到Mediaprojection。
4.设置VirtualDisplay 将图像和展示的View关联起来。一般来说我们会将图像展示到SurfaceView,这里为了为了便于拿到截图,我们使用ImageReader,他内置有SurfaceView。
5.通过ImageReader拿到截图
strDate = dateFormat.format(
new
java.util.Date());
nameImage = pathImage+strDate+
".png"
;
Image image = mImageReader.acquireLatestImage();
int
width = image.getWidth();
int
height = image.getHeight();
final
Image.Plane[] planes = image.getPlanes();
final
ByteBuffer buffer = planes[
0
].getBuffer();
int
pixelStride = planes[
0
].getPixelStride();
int
rowStride = planes[
0
].getRowStride();
int
rowPadding = rowStride - pixelStride * width;
Bitmap bitmap = Bitmap.createBitmap(width+rowPadding/pixelStride, height, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
bitmap = Bitmap.createBitmap(bitmap,
0
,
0
,width, height);
image.close();
6.截屏之后关闭VirtualDisplay 。
需要注意的一点是,acquireLatestImage,acquireNextImage两个方法如果循环调用的话会占用大量的系统资源,有因为这是一个service而容易被人忽略这一点,所以一定要去做相关的触发控制。具体办法可以根据官网提供的两个函数的特性来设计实现。
二 图像的预处理
各个平台的扑克牌长得都不一样,下面5张是我挑选出来比较有代表性问题的图片。
第一张 干扰多
第二张 扁平严重,图像模糊
第三张 花色和数字轻度粘连
第四张 数字和其他的图形粘连
第五张:特效的动态干扰
解决方法:
1.基于扑克牌的红、黑、特性,重新设计灰度化算法,达到加强红黑特征的目的。
2.设计平滑算子,对图片的粘连处进行平滑,达到分割轻度粘连的目的。
3.根据不同阈值(加上形态学处理)形成多张二值化图片
4.轮廓提取并进行合并,过滤。
下面是典型的经过处理后的轮廓图片:
至此,我们寻找普通牌轮廓的目的基本达成。我们看看有各种问题的图片表现如何。
待续。。。