专栏名称: 51CTO
51CTO官方公众号——聚焦最新最前沿最有料的IT技术资讯、IT行业精华内容、产品交流心得。本订阅号为大家提供各种技术干货,还会不定期的举办有奖活动,敬请关注。
目录
相关文章推荐
51好读  ›  专栏  ›  51CTO

国外程序员真会玩,他用这个技术整蛊了全公司的人…

51CTO  · 公众号  · 科技媒体  · 2017-03-13 11:55

正文

我喜欢用Photoshop修改各种东西,再把结果在Slack公司内发布,每次都能带来新的想法我享受在其中。


不过重复打开Photoshop再复制/粘贴面部图像确实相当乏味。

在最初产生这个想法时,我就意识到这个项目将主要包含三大组成部分:

  1. 简单图像修改

  2. Slack集成

  3. 面部检测


以往我曾经使用过Go中的image与image/draw软件包,并阅读过与之相关的几篇文章,因此我对于完成这项任务很有信心。组成部分1就此搞定。


我还曾经在Go中构建过一款玩具性质的Slack机器人,其中用到了查找自谷歌的几条指令。虽然缺少Go Slack官方整体客户端会让问题变得更为复杂,但出于最基本的需求,我相信自己能够完成通过Slack下载及上传图像这样一项工作。组成部分2也就不是问题了。


我唯一不确定的是面部检测工作到底是否易于实现。我在谷歌上查找golang面部检测内容,并点开第一条结果,其内容指向StackOverflow上关于go-opencv计算机视觉库的一条问题。在查阅了该库中的面部检测示例项目后,我了解到了自己需要掌握的一切。组成部分3也同样得到了解决。


面部检测

由于熟悉度最低,所以我决定首先从面部检测入手。这是项目中最大的难题,因此我打算先看看自己能否搞定,如果不行那其它的工作都将毫无意义。


我决定尽可能对go-opencv库进行封装。可以肯定的是,opencv数据类型与Go标准库有所区别,至少在其定义Image与Rectangle两项接口方面存在差异,因此必须作出一些调整。


我在其中发现一项对opencv.FromImage方法的引用,其负责将Go的image.Image转换为opencv库的形式。这意味着我不再需要将文件路径传递至opencv.LoadImage方法以进行转换,而可以直接处理存储在内存中的镜像。这能够节约从Slack接收图像后将其保存在文件系统中的步骤。


遗憾的是,我无法利用同样的转换方式加载Haar面部识别XML文件,不过这样的结果我还可以接受,所以暂时先这样吧。


以此为基础,我编写出了以下 facefinder包( 可在电脑端查看完整代码,下同

  1. package facefinder import ( "image""github.com/lazywei/go-opencv/opencv" ) var faceCascade *opencv.HaarCascade type Finder struct { cascade *opencv.HaarCascade } func NewFinder(xml string) *Finder { return &Finder{ cascade: opencv.LoadHaarClassifierCascade(xml), } } func (f *Finder) Detect(i image.Image) []image.Rectangle { var output []image.Rectangle faces :f.cascade.DetectObjects(opencv.FromImage(i)) for _, face :range faces { output = append(output, image.Rectangle{ image.Point{face.X(), face.Y()}, image.Point{face.X() + face.Width(), face.Y() + face.Height()}, }) } return output } 


而后,我能够轻松找到图像中的 面部区域

  1. imageReader, _ :os.Open(imageFile) baseImage, _, _ :image.Decode(imageReader) finder :facefinder.NewFinder(haarCascadeFilepath) faces :finder.Detect(baseImage) for _, face :range faces { // [...] } 


我从谷歌上复制了几段“绘制矩形”代码以进行功能检查,并确定以上代码确实能够正常工作。有了位置信息,我又鼓捣出一条 图像加载转换函数 (其中更关注错误内容,而非急于将一切塞进)。

  1. func loadImage(file string) image.Image { reader, err :os.Open(file) if err != nil { log.Fatalf("error loading %s: %s", file, err) } img, _, err :image.Decode(reader) if err != nil { log.Fatalf("error loading %s: %s", file, err) } return img } 


图像修改

接下来,我 的新循环如下所示:

  1. baseImage :loadImage(imageFile) chrisFace :loadImage(chrisFaceFile) bounds :baseImage.Bounds() finder :facefinder.NewFinder(haarCascadeFilepath) faces :finder.Detect(baseImage) // Convert image.Image to a mutable image.ImageRGBA canvas :image.NewRGBA(bounds) draw.Draw(canvas, bounds, baseImage, bounds.Min, draw.Src) for _, face :range faces { draw.Draw( canvas, face, chrisFace, bounds.Min, draw.Src, ) } 







请到「今天看啥」查看全文