好吧,其实整个都是建立在face++的基础上的,没有任何技术含量,我只是个勤劳的搬运工。
微信公众平台,怎么说呢,还是传统的一问一答的形式,你发个信息过来,我收到了处理下,再给你回馈一条信息,就是这么简单。
简单的你来我往
先说信息互传的问题,微信公众平台是post过来一个xml,服务器端打包一个xml发回去。
从最简单的,直接把用户信息返回去搞起吧。
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[this is a test]]></Content> <MsgId>1234567890123456</MsgId> </xml>
参数 | 描述 |
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | text |
Content | 文本消息内容 |
MsgId | 消息id,64位整型 |
相应的数据结构也就自然出来了:
type Request struct{ ToUserName string FromUserName string CreateTime time.Duration MsgType string Content string MsgId int }
将输入的xml解码:
func decodeRequest(data []byte)(req *Request,err error){ req=&Request{} err=xml.Unmarshal(data,req) return }
虽然微信服务器是用post方式传递的数据,不过实际还通过url传递过来了三个参数:signature,timestamp,nonce.
这三个参数可以验证消息是否微信服务器发送过来的。
取post过来的数据:
func Action(w http.ResponseWriter,r *http.Request){ postedMsg,err:=ioutil.ReadAll(r.Body) if err!=nil{ log.Fatal(err) } r.Body.Close() msg,err:=decodeRequest(postedMsg) ... }
接下来就是回复信息
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>12345678</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[content]]></Content> <FuncFlag>0</FuncFlag> </xml>
参数 |
描述 |
ToUserName | 接收方帐号(收到的OpenID) |
FromUserName |
开发者微信号 |
CreateTime | 消息创建时间 |
MsgType |
text |
Content | 回复的消息内容,长度不超过2048字节 |
FuncFlag | 位0x0001被标志时,星标刚收到的消息 |
简单封装下:
type Response struct{ XMLName xml.Name `xml:"xml"` ToUserName string FromUserName string CreateTime time.Duration MsgType string Content string FuncFlag int } func encodeResponse(resp Response)(data []byte,err error){ resp.CreateTime=time.Second data,err=xml.Marshal(resp) return }
将数据发送回去的代码:
var resp Response resp.ToUserName=msg.FromUserName resp.FromUserName=msg.ToUserName resp.MsgType="text" resp.Content=msg.Content resp.FuncFlag=0 respData,err:=encodeResponse(resp) fmt.Fprintf(w,string(respData))
人脸识别
这个怎么说,就是用户通过微信发送照片,照片是存到微信服务器的,微信给我发一个图片url,我再把这个url转给face++,face++将分析结果给我发回来,我再把这些数据简单处理下,反馈给微信用户(当然,中间还隔了层微信服务器)。
整个过程中,我所做的就是简单的json数据处理,什么高端的图像处理什么的都跟我不沾边,哈哈~
首先当然是到http://cn.faceplusplus.com/注册,获取API_SECRET、API_KEY。
而后推荐看文档,http://cn.faceplusplus.com/dev/getting-started/api2info/,当然直接跟着我来一遍也行。
先来个人脸检测吧,检测出性别、年龄、种族。
看了示例文档后,发现detect调用后返回的json的结构表示出来大概是这样:
type Faceslice struct{ Face []struct{ Attribute struct{ Age struct{ Range float64 Value float64 } Gender struct{ Confidence float64 Value string } Race struct{ Confidence float64 Vaule string } } Face_id string Position struct{ Center struct{ X float64 Y float64 } Eye_left struct{ X float64 Y float64 } Eye_right struct{ X float64 Y float64 } Height float64 Mouth_left struct{ X float64 Y float64 } Mouth_right struct{ X float64 Y float64 } Nose struct{ X float64 Y float64 } Width float64 } Tag string } Img_height int Img_id string Img_width int Session_id string url string }
解析json数据:
func DecodeDetect(data []byte) Faceslice{ var f Faceslice json.Unmarshal(data,&f) return f }
接着还是来写个get函数吧:
func get(url string)(b []byte,err error){ res,e:=http.Get(url) if e!=nil{ err=e return } data,e:=ioutil.ReadAll(res.Body) if e!=nil{ err=e return } res.Body.Close() return data,nil }
调用face++接口并返回相应的数据:
const apiurl="https://apicn.faceplusplus.com" func DetectionDetect(picurl string)detection.Faceslice{ url:=apiurl+"/v2/detection/detect?url="+picurl+"&api_secret="+apisecret+"&api_key="+apikey tmp,_:=get(url) return detection.DecodeDetect(tmp) }
刚刚上面的示例只是简单考虑了文本信息,现在要传递的是图片信息,所以做个简单的修改:
type Request struct{ ToUserName string FromUserName string CreateTime time.Duration MsgType string Content string PicUrl string MsgId int }
Action函数里也该有所修改,判定下msg.MsgType,如果是text,则跟刚才一样处理,如果是image,则有新的处理方法。
我一个就做了两个简单的处理,一个是年龄、性别、种族,还有就是如果照片里是两个人,则给出五官及整体的相似度值。
相似度的代码直接放下面吧:
package recognition import( "encoding/json" ) type Compare struct{ Component_similarity struct{ Eye float64 Mouth float64 Nose float64 Eyebrow float64 } Session_id string Similarity float64 } func DecodeCompare(data []byte)Compare{ var c Compare json.Unmarshal(data,&c) return c }
func RecognitionCompare(face1id,face2id string)recognition.Compare{ url:=apiurl+"/v2/recognition/compare?api_secret="+apisecret+"&api_key="+ apikey+"&face_id2="+face2id+"&face_id1="+face1id tmp,_:= get(url) return recognition.DecodeCompare(tmp) }
判定图片里有几个人,一个人输出性别、年龄,两个人输出相似度,三个及以上,暂未判定:
if msg.MsgType=="image"{ var faceslice detection.Faceslice faceslice=facepp.DetectionDetect(msg.PicUrl) switch len(faceslice.Face){ case 0: resp.Content="请上传有脸的人物照片!" case 1: attribute:=faceslice.Face[0].Attribute age:=attribute.Age gender:=attribute.Gender var faceGender string if gender.Value=="Male"{ faceGender="男" }else{ faceGender="女" } faceAgeValue:=fmt.Sprintf("%d",int(age.Value)) faceAgeRange:=fmt.Sprintf("%d",int(age.Range)) resp.Content="性别:"+faceGender+"\n"+"年龄:"+faceAgeValue+"(±"+faceAgeRange+")" case 2: face1id:=faceslice.Face[0].Face_id face2id:=faceslice.Face[1].Face_id var compare recognition.Compare compare=facepp.RecognitionCompare(face1id,face2id) resp.Content="眼睛相似度:"+fmt.Sprintf("%f",compare.Component_similarity.Eye)+"\n"+"嘴巴相似度:"+fmt.Sprintf("%f",compare.Component_similarity.Mouth)+"\n"+"鼻子相似度:"+fmt.Sprintf("%f",compare.Component_similarity.Nos e)+"\n"+"眉毛相似度:"+fmt.Sprintf("%f",compare.Component_similarity.Eyebrow)+"\n"+"整体相似度:"+fmt.Sprintf("%f",compare.Similarity) default: resp.Content="照片里人物太多了,暂不分析!" } }