资讯专栏INFORMATION COLUMN

Java快速开发第三方——腾讯人工智能AI接入详解(大专狗终章)

evin2016 / 2418人阅读

摘要:微信接入采用的第三方接入微信,具体功能接入还是要看微信公众号开发文档,架构则是上面所说的。吐槽一下,腾讯包括微信,他们的文档确实没有阿里做得好。人脸融合结尾这一块腾讯人工智能接入已解释完毕。

要想骑自行车,首先不是要学会如何造自行车,而是学会如何骑行

前言

结合腾讯AI开放平台群里的demo解析,QQ群号:581197347。
这个项目是我只花费两天时间做完的,采用的技术是SpringBoot+SpringCloud+MongoDB。
为啥只要两天呢,现在是微服务快速开发时代,两天开发一个小型项目真的自己都觉得花费时间太长。
前端框架采用Layui,如果是微信访问则会跳到微信端的页面。微信接入采用GitHub的第三方接入微信,
具体功能接入还是要看微信公众号开发文档,架构则是上面所说的SpringBoot+SpringCloud。数据库方面采用MongoDB,因为我只需要存图片文件不需要采集用户信息。

项目

这是现在上线的项目,您也可以微信扫码访问,都是同一个链接。点击访问项目
在iphone这边试了下人脸融合好像不行。因为微信jdk要另外设置。没ios机...

为什么选择腾讯

这里主要详解如何接入腾讯第三方AI,其他会简单带过:腾讯AI官网
为什么选择腾讯的?而不是选择阿里,网易,百度呢?主要他们都要认证,要钱。(贫穷限制我的选择)。

开玩笑,言归正传,这是因为开发简洁通用,只要会一种,其他就换汤不换药。唯一的缺点就是他不保证并发。如果你只需要一种识别,并且是稳定高并发的,我建议阿里哈。
腾讯AI技术文档这个文档的接口鉴权也就是获取sign我是不建议你们去看的,因为都不知道他在说什么奖杯,我在这里跟大家讲如何获得sign,后面的接入就可以按照文档来操作了。
ps:吐槽一下,腾讯包括微信,他们的文档确实没有阿里做得好。

开始接入

首先在这里,我说明一下,我会将他作为一个main主函数来运行。我也只讲一种方式,其他你真的看懂了,自然而然就会运用了。

首先要知道如何获得签名也就是我们所谓的Sign和要求计算的参数
你可以根据官方文档看到我们需要通过这几个参数来计算出签名,然而他们并没有说明这个text:腾讯AI开放平台是什么,这个text就是你要接入哪种识别另外要加入的参数,就比如这里我讲解人脸融合,人脸融合根据文档要多传一个model参数,也就是说这个model也要加入Sign的计算当中。

那么我们知道了sign需要的参数,我们要怎么计算呢。

直接看文档对于初学者来说,看了等于白看,文档说要

1. 获得参数对列表N(字典升级排序)。
2. 按URL键值拼接字符串T,参数对列表N的参数对进行URL键值拼接,值使用URL编码,URL编码算法用大写字母。
3. 拼接应用密钥,得到字符串S。
4. 计算MD5摘要,得到签名字符串。
说实话,第一次我看这个文档我的main函数运行的不少于100遍的出错。那要怎么接入呢?
如果你看的懂文档,请直接略过这端Sign签名
获得Sign

时间戳time_stamp和随机字符串nonce_str我们直接可以自己生成的。

String time_stamp = System.currentTimeMillis()/1000+"";
String nonce_str = TencentAISign.getRandomString(10);

还有image属性也就是你上传的照片,腾讯限定了图片大小(根据文档)和要求原始图片的base64编码数据
如何限定图片大小就是你的事了,在这里将如何将图片进行base64编码,编码这里我讲两种,一种是本地路径编码,另一种根据Url网络资源编码
这里我写一个工具类,可以根据本地或者Url分别进行base64编码

public class UrlMethodUtil {
    public static byte[] local2byte(String url)throws Exception{  //由本地路径得到byte
        byte [] imageData = FileUtil.readFileByBytes(url);
        return imageData;
    }
    public static byte[] url2byte(String url)throws Exception {  //由url得到byte
        byte [] imageData =  IoUtil.getImageFromNetByUrl(url);
        return imageData;
    }
}

FileUtil 就是从本地路径获得byte[]数据

public class FileUtil {
    /**
     * 根据文件路径读取byte[] 数组
     */
    public static byte[] readFileByBytes(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            throw new FileNotFoundException(filePath);
        } else {
            ByteArrayOutputStream bos = new ByteArrayOutputStream((int) file.length());
            BufferedInputStream in = null;

            try {
                in = new BufferedInputStream(new FileInputStream(file));
                short bufSize = 1024;
                byte[] buffer = new byte[bufSize];
                int len1;
                while (-1 != (len1 = in.read(buffer, 0, bufSize))) {
                    bos.write(buffer, 0, len1);
                }
                byte[] var7 = bos.toByteArray();
                return var7;
            } finally {
                try {
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException var14) {
                    var14.printStackTrace();
                }
                bos.close();
            }
        }
    }
}

IoUtil就是从Url获得byte[]数据

public class IoUtil {
    /**
     * 根据地址获得数据的字节流
     * @param strUrl 网络连接地址
     * @return
     */
    public static byte[] getImageFromNetByUrl(String strUrl){
        try {
            URL url = new URL(strUrl);
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(10 * 1000);
            InputStream inStream = conn.getInputStream();//通过输入流获取图片数据
            byte[] btImg = readInputStream(inStream);//得到图片的二进制数据
            return btImg;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 从输入流中获取数据
     * @param inStream 输入流
     * @return
     * @throws Exception
     */
    public static byte[] readInputStream(InputStream inStream) throws Exception{
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while( (len=inStream.read(buffer)) != -1 ){
            outStream.write(buffer, 0, len);
        }
        inStream.close();
        return outStream.toByteArray();
    }
}

然后就可以很轻松的调用了:

byte [] imageData = UrlMethodUtil.local2byte("E:/demo.png");//本地图片
byte [] imageData = UrlMethodUtil.url2byte("网络资源路径Url");

得到byte数组了我们就可以根据Base64的工具类转化,这里我贴出来,百度一堆

public class Base64Util {
    private static final char last2byte = (char) Integer.parseInt("00000011", 2);
    private static final char last4byte = (char) Integer.parseInt("00001111", 2);
    private static final char last6byte = (char) Integer.parseInt("00111111", 2);
    private static final char lead6byte = (char) Integer.parseInt("11111100", 2);
    private static final char lead4byte = (char) Integer.parseInt("11110000", 2);
    private static final char lead2byte = (char) Integer.parseInt("11000000", 2);
    private static final char[] encodeTable = new char[]{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/"};
    public Base64Util() {
    }
    public static String encode(byte[] from) {
        StringBuilder to = new StringBuilder((int) ((double) from.length * 1.34D) + 3);
        int num = 0;
        char currentByte = 0;
        int i;
        for (i = 0; i < from.length; ++i) {
            for (num %= 8; num < 8; num += 6) {
                switch (num) {
                    case 0:
                        currentByte = (char) (from[i] & lead6byte);
                        currentByte = (char) (currentByte >>> 2);
                    case 1:
                    case 3:
                    case 5:
                    default:
                        break;
                    case 2:
                        currentByte = (char) (from[i] & last6byte);
                        break;
                    case 4:
                        currentByte = (char) (from[i] & last4byte);
                        currentByte = (char) (currentByte << 2);
                        if (i + 1 < from.length) {
                            currentByte = (char) (currentByte | (from[i + 1] & lead2byte) >>> 6);
                        }
                        break;
                    case 6:
                        currentByte = (char) (from[i] & last2byte);
                        currentByte = (char) (currentByte << 4);
                        if (i + 1 < from.length) {
                            currentByte = (char) (currentByte | (from[i + 1] & lead4byte) >>> 4);
                        }
                }
                to.append(encodeTable[currentByte]);
            }
        }
        if (to.length() % 4 != 0) {
            for (i = 4 - to.length() % 4; i > 0; --i) {
                to.append("=");
            }
        }
        return to.toString();
    }
}

这样image的Base64编码搞定,终于拿到了image属性了,接下来就可以准备开始签名啦
我们现在拥有的属性有:appid,appKey,time_stamp,nonce_str,image。再根据我们要实现的功能需要的参数终于可以实现签名啦。感觉好麻烦有木有。确实是挺麻烦的,但这也是一种安全验证的过程。
那我们怎么将这么多参数放在一起计算出Sign呢?答案可想而知,用Map。

        Map person_Id_body = new HashMap<>();
        person_Id_body.put("app_id", String.valueOf(TencentAPI.APP_ID_AI));
        person_Id_body.put("time_stamp",time_stamp);
        person_Id_body.put("nonce_str", nonce_str);
        person_Id_body.put("image", img64);
        person_Id_body.put("model","1");  //这是人脸融合需要的参数,1表示模板1.

在这里我将用在腾讯公众平台群一位大神发出来的通用签名工具类来签名

public class TencentAISignSort {
    /**
     * SIGN签名生成算法-JAVA版本 通用。默认参数都为UTF-8适用
     * @param HashMap params 请求参数集,所有参数必须已转换为字符串类型
     * @return 签名
     * @throws IOException
     */
    public static String getSignature(Map params) throws IOException {
            // 先将参数以其参数名的字典序升序进行排序
            Map sortedParams = new TreeMap<>(params);
            Set> entrys = sortedParams.entrySet();
            // 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
            StringBuilder baseString = new StringBuilder();
            for (Map.Entry param : entrys) {
                //sign参数 和 空值参数 不加入算法
                if(param.getValue()!=null && !"".equals(param.getKey().trim()) && !"sign".equals(param.getKey().trim()) && !"".equals(param.getValue().trim())) {
                    baseString.append(param.getKey().trim()).append("=").append(URLEncoder.encode(param.getValue().trim(),"UTF-8")).append("&");
                }
            }
            System.err.println("未拼接APPKEY的参数:"+baseString.toString());
            if(baseString.length() > 0 ) {
                baseString.deleteCharAt(baseString.length()-1).append("&app_key="+TencentAPI.APP_KEY_AI);
            }
            System.err.println("拼接APPKEY后的参数:"+baseString.toString());
            // 使用MD5对待签名串求签
            try {
                String sign = MD5.getMD5(baseString.toString());
                return sign;
            } catch (Exception ex) {
                throw new IOException(ex);
            }
        }
    /**
     * SIGN签名生成算法-JAVA版本 针对于基本文本分析接口要求text为GBK的方法
     * @param HashMap params 请求参数集,所有参数必须已转换为字符串类型
     * @return 签名
     * @throws IOException
     */
    public static String getSignatureforNLP(HashMap params) throws IOException {
            // 先将参数以其参数名的字典序升序进行排序
            Map sortedParams = new TreeMap<>(params);
            Set> entrys = sortedParams.entrySet();
            // 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
            StringBuilder baseString = new StringBuilder();
            for (Map.Entry param : entrys) {
                
                //sign参数 和 空值参数 不加入算法
                if(param.getValue()!=null && !"".equals(param.getKey().trim()) && !"sign".equals(param.getKey().trim()) && !"".equals(param.getValue().trim())) {
                    if(param.getKey().equals("text")){
                        baseString.append(param.getKey().trim()).append("=").append(URLEncoder.encode(param.getValue().trim(),"GBK")).append("&");
                    }else{
                        baseString.append(param.getKey().trim()).append("=").append(URLEncoder.encode(param.getValue().trim(),"UTF-8")).append("&");
                        
                    }
                }
            }
            if(baseString.length() > 0 ) {
                baseString.deleteCharAt(baseString.length()-1).append("&app_key="+TencentAPI.APP_KEY_AI);
            }
            // 使用MD5对待签名串求签
            try {
                String sign = MD5.getMD5(baseString.toString());
                return sign;
            } catch (Exception ex) {
                throw new IOException(ex);
            }
        }
    /**
     * 获取拼接的参数
     * @param params
     * @return
     * @throws IOException
     */
    public static String getParams(HashMap params) throws IOException {
        //  先将参数以其参数名的字典序升序进行排序
        Map sortedParams = new TreeMap<>(params);
        Set> entrys = sortedParams.entrySet();
        // 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
        StringBuilder baseString = new StringBuilder();
        for (Map.Entry param : entrys) {
            //sign参数 和 空值参数 不加入算法
           baseString.append(param.getKey().trim()).append("=").append(URLEncoder.encode(param.getValue().trim(),"UTF-8")).append("&");
        }
       return baseString.toString();
    }
}

这样简单一句话就实现了签名啦:

String sign = TencentAISignSort.getSignature(person_Id_body);

向API发送请求
    person_Id_body.put("sign", sign);  //将Sign也放入Map中
    //这我就不用说了吧,这是头信息需要的
    Map headers = new HashMap<>();   //headers头
    headers.put("Content-Type", "application/x-www-form-urlencoded");
    HttpResponse responseBD = HttpsUtil4Tencent.doPostTencentAI(TencentAPI.FACEMERGE, headers, person_Id_body);
        String json = EntityUtils.toString(responseBD.getEntity());
        System.out.println(json);  //这个就是我们的要的数据了

ps:一堆工具类对不对啊哈哈哈,这才叫快速开发。

//HttpsUtil4Tencent工具类:
public class HttpsUtil4Tencent {
    private static HttpClient wrapClient(String host) {
        HttpClient httpClient = new DefaultHttpClient();
        if (host.startsWith("https://")) {
            sslClient(httpClient);
        }
        return httpClient;
    }
    private static void sslClient(HttpClient httpClient) {
        try {
            SSLContext ctx = SSLContext.getInstance("TLS");
            X509TrustManager tm = new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
                public void checkClientTrusted(X509Certificate[] xcs, String str) {
                    
                }
                public void checkServerTrusted(X509Certificate[] xcs, String str) {
                    
                }
            };
            ctx.init(null, new TrustManager[] { tm }, null);
            SSLSocketFactory ssf = new SSLSocketFactory(ctx);
            ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            ClientConnectionManager ccm = httpClient.getConnectionManager();
            SchemeRegistry registry = ccm.getSchemeRegistry();
            registry.register(new Scheme("https", 443, ssf));
        } catch (KeyManagementException ex) {
            throw new RuntimeException(ex);
        } catch (NoSuchAlgorithmException ex) {
            throw new RuntimeException(ex);
        }
    }
    /**
     * 
     * @Title doPostBD
     * @param url 接口地址
     * @param method 请求方式
     * @param headers 
     * @param bodys
     * @return response
     * @throws Exception
     * @author 小帅帅丶
     * @date 2017-3-20
     *
     */
    public static HttpResponse doPostTencentAI(String url, 
            Map headers, 
            Map bodys)
            throws Exception {        
        HttpClient httpClient = wrapClient(url);
        HttpPost request = new HttpPost(url);
        for (Map.Entry e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }
        if (bodys != null) {
            List nameValuePairList = new ArrayList();
            for (String key : bodys.keySet()) {
                nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key)));
            }
            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList);
            formEntity.setContentType("application/x-www-form-urlencoded;charset=UTF-8");
            request.setEntity(formEntity);
        }
        return httpClient.execute(request);
    }
 
}

得到的json就是腾讯给我们的数据。那我们要怎么把json数据转为对象呢。
我的另一篇文章就写了:Java 跨域 Json字符转类对象
在人脸识别这块,他回应的是一串base64的数据,我们应该怎么转为图片呢

     String xmlImg = persion_id.getImage();
                response.setContentType("image/*"); // 设置返回的文件类型
                OutputStream toClient = response.getOutputStream();
                IoUtil.GenerateImage(xmlImg,toClient);

没错,还是工具类IoUtil。

public class TencentAPI {
    //自己的APPID
    public static final Integer APP_ID_AI = 0;
    //自己的APPKEY
    public static final String APP_KEY_AI = "******";
    public static final String PERSON_ID = "https://api.ai.qq.com/fcgi-bin/ocr/ocr_idcardocr";  //身份证识别
    public static final String PHOTO_SPEAK = "https://api.ai.qq.com/fcgi-bin/vision/vision_imgtotext"; //看图说话
    public static final String SCENE_RECOGNITION  = "https://api.ai.qq.com/fcgi-bin/vision/vision_scener"; //场景识别:对图行进行场景识别,快速找出图片中包含的场景信息
    public static final String OBJECT_RECOGNITION = "https://api.ai.qq.com/fcgi-bin/vision/vision_objectr"; //物体识别:对图行进行物体识别,快速找出图片中包含的物体信息
    public static final String IMAGE_LABEL = "https://api.ai.qq.com/fcgi-bin/image/image_tag"; //图像标签识别:识别一个图像的标签信息,对图像分类。
    public static final String FACEMERGE="https://api.ai.qq.com/fcgi-bin/ptu/ptu_facemerge";  //人脸融合
}
结尾

这一块腾讯人工智能AI接入已解释完毕。如有什么不懂得可以来问下我。我的邮箱是519286925@qq.com
为什么说这是大专狗终章呢,因为我已经大三了,这是我最后一篇在大专学校写的文章。即将面临的面试工作或者插本,前方的路如何,我将何去何从。请等我下篇文章:【插本狗初章】或者【工作狗初章】

世上无难事,只有你会不会,想不想学。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70743.html

相关文章

  • 【Taip】Java快速开发腾讯人工智能AI接入

    摘要:鉴权实现快速开发第三方腾讯人工智能接入详解形式目前已经接入文字识别语音识别接口服务调用服务项目结构介绍基类通信相关类签名公用类类类工具类使用引入即可示例代码是调用腾讯中的客户端,为调用腾讯中功能的开发人员提供了一系列的交互方法。 TAIP 是调用腾讯 AI 接口的 Java 客户端,为调用腾讯 AI 功能的开发人员提供了一系列的交互方法。 Java调用腾讯AI接口服务。鉴权实现Java...

    winterdawn 评论0 收藏0
  • BAT的云计算布局

    摘要:提到的云计算布局,就不得不提到年月中国领袖峰会上,三位掌门人针对云计算的同台论道。且从官方有限的披露资料而言,很难对现阶段的百度云计算独立做评判。该计划提出的发展目标是到年,我国云计算产业规模达到亿元。提到BAT的云计算布局,就不得不提到2010年3月中国IT领袖峰会上,BAT三位掌门人针对云计算的同台论道。坐在台上的百度和腾讯创始人相继发言,李彦宏不客气的说,云计算好比新瓶装旧酒,技术上没...

    justCoding 评论0 收藏0
  • 道器相融,由Angel论一个优秀机器学习平台的自我修养

    摘要:而道器相融,在我看来,那炼丹就需要一个好的丹炉了,也就是一个优秀的机器学习平台。因此,一个机器学习平台要取得成功,最好具备如下五个特点精辟的核心抽象一个机器学习平台,必须有其灵魂,也就是它的核心抽象。 *本文首发于 AI前线 ,欢迎转载,并请注明出处。 摘要 2017年6月,腾讯正式开源面向机器学习的第三代高性能计算平台 Angel,在GitHub上备受关注;2017年10月19日,腾...

    leo108 评论0 收藏0

发表评论

0条评论

evin2016

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<