首页 » 人工智能 » 国外轨范员真会玩他用这个技能整蛊了全公司的人……

国外轨范员真会玩他用这个技能整蛊了全公司的人……

中建深圳装饰通讯 2024-12-24 0

扫一扫用手机浏览

文章目录 [+]

不过重复打开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包:

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 }

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

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

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

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 }

图像修正

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

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, ) }

令人振奋,测试结果统统顺利。

言归正传,其首次实际效果就远超我的预期。
矩形绘制算法真棒!

在图像修正方面,我首先得想办法去掉玄色背景。
我以前曾利用过PNG合营透明背景的方法,因此确信其一定有效。
在谷歌了几下后,我有时创造了draw.Draw函数中的draw.Over。
我将其塞进正在利用的draw.Src,确实有效!

虽然也可以用羽羊毫逐步绘边,但脑袋里的一个声音见告我,差不多就可以了。

好的,接下来我须要把面部图像缩小一点。
可以肯定的是,如果将面部图像放进尺寸完备相同的矩形,那么二者肯定无法匹配。
这只是一款面部检测工具,而非头部检测工具,这意味着我得到的矩形并不适用于更换全体头部。
我编写了一条快速函数以为image.Rectangle增加特定空缺边缘,终极将详细值设定为30%。

完成后,我开始对图像进行大小/匹配调度。
终极,我选择了disintegration/imaging,其拥有一条大略的imaging.Fit函数且供应水平镜像等其它转换操作。
我的面部源图像不多,以是我想这种镜像功能可以供应多一种图像选择。

在导入后,我的新循环如下所示:

for _, face := range faces { // Pad the rectangle by 30 percent rect := rectMargin(30.0, face) // Grab a random face (also 50/50 chance it's mirrored) newFace := chrisFaces.Random() chrisFace := imaging.Fit(newFace, rect.Dx(), rect.Dy(), imaging.Lanczos) draw.Draw( canvas, rect, chrisFace, bounds.Min, draw.Over, ) }

我又进行了一轮新的测试,效果相称不错!

到这里,我意识到自己做出了一些真正有代价的东西。

Slack集成

我把面部修正代码转化为一个可运行的二进制文件,并打算将其打包成一个Slack机器人。
之以是先转换为二进制形式,是为了方便测试并在确定统统无误后再行打包。
现在机遇已经成熟,我将把它变成Slack机器人。

当然,由于个人水平的限定,我又转向了谷歌。

第一条结果便是我所须要的内容。
我花了大量韶光阅读Slack的API解释文档并加以实践,终极我得到了以下结果:

不错

第一套迭代利用了Slack上传,但其作为自由Slack层意味着其不足空想。
我转而将输出结果以本地办法存储在自己的做事器上,而后再将其链至Slack。
由于Slack会自动扩展大部分图像链接,因此这种作法对大多数人来说并不会影响到用户体验,也不会引来顶头上司的把稳。

由于访问过程更为轻松,现在我能够快速得到大量实验性面部图像。
我意识到,如果其找不到任何面部图像,则会全程回答同样的原有图像——这就不好玩了。
以是我将循环调度为:

iflen(faces) == 0 { // Grab a specific face and resize it to 1/3 the width// of the base image face := imaging.Resize( chrisFaces[0], bounds.Dx()/3, 0, imaging.Lanczos, ) face_bounds := face.Bounds() draw.Draw( canvas, bounds, face, // I'll be honest, I was a couple beers in when I came up with this and I// have no idea how it works exactly, but it puts the face at the bottom of// the image, centered horizontally with the lower half of the face cut off bounds.Min.Add(image.Pt( -bounds.Max/2+face_bounds.Max.X/2, -bounds.Max.Y+int(float64(face_bounds.Max.Y)/1.9), )), draw.Over, ) }

现在的结果是:

我个人对这套办理方案非常满意。

到这里全部事情已经就绪,就等同事们的反应了。
我只用了一个晚上就完备了从观点到原型的全部事情,没人知道我为他们准备了若何的惊喜。

截至目前,我的经理是最为积极的Chrisbot手动配置用户。

抱歉了Mat,看来自动化方案终极一定会取代人类的职位。

但这家伙自己则非常愉快。

不久之后,全体办公室都在向@Chrisbot发送图片。

我惊喜地创造,它确实能够精确地处理面部重叠情形,即首先绘制最远处的面孔。
虽然这纯粹属于go-opencv库返回矩形时实际顺序带来的副浸染,但我对结果非常满意。

不过虽然自动化面部更换大大增加了Slack当中Chris的亮相次数,但仍有一些人认为,人为操作的结果更有灵性一些。

不得不承认,他们的不雅观点确实站得住脚——至少在某些情形之下。

原文链接:http://blog.zikes.me/post/how-i-ruined-office-productivity-with-a-slack-bot/

原文标题:How I Ruined Office Productivity With a Face-Replacing Slack Bot

(Without Really Knowing What I Was Doing)

核子可乐译

【51CTO翻译稿件,互助站点转载请注明原文作者和出处为51CTO.com】

标签:

相关文章