개발 가이드

음성인식 API 란?

최신 인공지능 기술에 기반하여 한국어 및 영어에 대해 고성능의 음성인식 정확률을 제공하는 서비스로서, 사용자가 발성한 녹음된 입력 음성 데이터(단위 파일 또는 버퍼)를 음성인식 서버로 전달하여 문자(텍스트)로 제공합니다. 음성인식 API는 HTTP 기반의 REST API 인터페이스로 JSON 포맷 기반의 입력 및 출력을 지원하며 ETRI에서 제공하는 Access Key 인증을 통해 사용할 수 있는 Open API 입니다.

사전 준비 사항

음성인식 API를 사용하려면 ETRI 인공지능 Open API 서비스에서 개발자 계정 등록을 해야 합니다. 개발자 계정 등록 이후 API 사용 신청을 통해 API 사용시 필요한 인증 정보를 획득해야 합니다.

  1. “ETRI AI” 홈페이지의 [Open API 사용신청] 페이지로 이동합니다.
  2. [Open API 사용신청] 페이지의 키 발급 신청하기 버튼을 클릭합니다.
  3. Open API의 Access Key 발급에 필요한 사용 신청 정보를 입력 후 등록하기 버튼을 클릭합니다.
    (등록된 사용 신청은 “ETRI AI” 관리자에 의해서 승인이 필요합니다.)
  4. [Open API 사용신청] 페이지의 키 발급 신청확인 버튼을 클릭합니다.
  5. 키 발급 신청에 입력한 “이메일”와 “패스워드”를 입력 후 API KEY 확인하기 버튼을 클릭합니다.
  6. 발급된 Open API의 Access Key를 확인합니다.
음성인식 API 사용하기

음성인식 API는 REST API이며, 음성인식에 사용하기 위해 샘플링 주파수(sampling rate 또는 sampling frequency) 16kHz로 녹음된 음성 파일을 Base64로 Encoding 하여 HTTP 통신으로 ETRI Open API 서버에 전달하면 됩니다. 서버가 제공하는 REST API의 URI는 다음과 같으며 POST 방식으로 호출해야 합니다.

http://aiopen.etri.re.kr:8000/WiseASR/Recognition

HTTP 요청으로 음성인식을 요청할 때 사전 준비 사항에서 발급받은 Access key 정보를 요청 본문에 포함시켜야 합니다. 다음은 HTTP 요청 메시지 예입니다.

[HTTP Request Body]
{
    "request_id": "reserved field",
    "access_key": “YOUR_ACCESS_KEY”,
    "argument": {
        "languageCode": “LANGUAGE_CODE”,
        "audio": "BASE64_OF_AUDIO_DATA"
    }
}
                            

위와 같은 HTTP 요청을 ETRI Open API 서버로 전달하면 서버는 JSON 형태의 Text 데이터를 HTTP 응답 메시지로 반환합니다. 다음은 HTTP 응답 예제 입니다.

[HTTP Response Header]
Access-Control-Allow-Origin:*
Connection:close
Content-Length:0
Content-Type:application/json; charset=UTF-8

[HTTP Response Body]
{
    "request_id": "reserved field",
    "result": 0,
    "return_object": {음성인식 결과 JSON}
}
                            
사전 준비 사항

음성인식 API를 사용하려면 ETRI 인공지능 Open API 서비스에서 개발자 계정 등록을 해야 합니다. 개발자 계정 등록 이후 API 사용 신청을 통해 API 사용시 필요한 인증 정보를 획득해야 합니다.

  1. “ETRI AI” 홈페이지의 [Open API 사용신청] 페이지로 이동합니다.
  2. [Open API 사용신청] 페이지의 키 발급 신청하기 버튼을 클릭합니다.
  3. Open API의 Access Key 발급에 필요한 사용 신청 정보를 입력 후 등록하기 버튼을 클릭합니다.
    (등록된 사용 신청은 “ETRI AI” 관리자에 의해서 승인이 필요합니다.)
  4. [Open API 사용신청] 페이지의 키 발급 신청확인 버튼을 클릭합니다.
  5. 키 발급 신청에 입력한 “이메일”와 “패스워드”를 입력 후 API KEY 확인하기 버튼을 클릭합니다.
  6. 발급된 Open API의 Access Key를 확인합니다.
음성인식용 안드로이드 앱 및 개발 환경

음성인식 및 발음평가용 android app 입니다.
아래의 설치 파일을 안드로이드 단말에 복사하여 설치한 후, 이용하시면 됩니다.

EtriOpenASR.apk

안드로이드 앱 제작에 참고가 가능한 소스코드 환경을 android studio 개발 환경으로 gitHub에 오픈하였으니 오픈 API를 사용한 app개발 시에 참고하시면 됩니다.
https://github.com/ETRISLP/EtriOpenASR

구현 예제
Java

JSON parsing을 위해 Gson 라이브러리를 사용하여 제공하고 있습니다. Gson 라이브러리에 대한 자세한 설명은 https://github.com/google/gson 에서 확인 하실 수 있습니다.

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;

import com.google.gson.Gson;

public class Example {

    static public void main ( String[] args ) {
        String openApiURL = "http://aiopen.etri.re.kr:8000/WiseASR/Recognition";
        String accessKey = "YOUR_ACCESS_KEY";    // 발급받은 Access Key
        String languageCode = "LANGUAGE_CODE";     // 언어 코드
        String audioFilePath = "AUDIO_FILE_PATH";  // 녹음된 음성 파일 경로
        String audioContents = null;

        Gson gson = new Gson();

        Map<String, Object> request = new HashMap<>();
        Map<String, String> argument = new HashMap<>();

        try {
            Path path = Paths.get(audioFilePath);
	        byte[] audioBytes = Files.readAllBytes(path);
            audioContents = Base64.getEncoder().encodeToString(audioBytes);
        } catch (IOException e) {
            e.printStackTrace();
        }

        argument.put("language_code", languageCode);
        argument.put("audio", audioContents);

        request.put("access_key", accessKey);
        request.put("argument", argument);

        URL url;
        Integer responseCode = null;
        String responBody = null;
        try {
            url = new URL(openApiURL);
            HttpURLConnection con = (HttpURLConnection)url.openConnection();
            con.setRequestMethod("POST");
            con.setDoOutput(true);

            DataOutputStream wr = new DataOutputStream(con.getOutputStream());
            wr.write(gson.toJson(request).getBytes("UTF-8"));
            wr.flush();
            wr.close();

            responseCode = con.getResponseCode();
            InputStream is = con.getInputStream();
            byte[] buffer = new byte[is.available()];
            int byteRead = is.read(buffer);
            responBody = new String(buffer);

            System.out.println("[responseCode] " + responseCode);
            System.out.println("[responBody]");
            System.out.println(responBody);

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
                                        
PHP
<?php
$openApiURL = "http://aiopen.etri.re.kr:8000/WiseASR/Recognition";
$accessKey = "YOUR_ACCESS_KEY";
$languageCode = "LANGUAGE_CODE";
$audioFilePath = "AUDIO_FILE_PATH";
$audioContents = base64_encode( file_get_contents($audioFilePath ) );

$request = array(
"access_key" => $accessKey,
    "argument" => array (
        "language_code" => $languageCode,
        "audio" => $audioContents
    )
);

try {
    $server_output = "";
    $ch = curl_init();
    $header = array(
        "Content-Type:application/json; charset=UTF-8",
    );
    curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    curl_setopt($ch, CURLOPT_URL, $openApiURL);
    curl_setopt($ch, CURLOPT_VERBOSE, true);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode ( $request) );

    $server_output = curl_exec ($ch);
    if($server_output === false) {
        echo "Error Number:".curl_errno($ch)."\n";
        echo "Error String:".curl_error($ch)."\n";
    }

    curl_close ($ch);
} catch ( Exception $e ) {
    echo $e->getMessage ();
}

echo "result = " . var_dump($server_output);
?>
                                        
C++

JSON parsing을 위해 jsoncpp 라이브러리를 사용하여 제공하고 있습니다. jsoncpp 라이브러리에 대한 자세한 설명은 https://github.com/open-source-parsers/jsoncpp 에서 확인 하실 수 있습니다.

HTTP 통신을 위해 curl 라이브러리를 사용하여 제공하고 있습니다. curl 라이브러리에 대한 자세한 설명은 https://curl.haxx.se/libcurl 에서 확인 하실 수 있습니다.

컴파일을 위해서는 아래와 같이 추가된 LIB에 대한 옵션을 추가해야 합니다.
g++ (c++파일명) (JOSNCPP)/json/json.h (JOSNCPP)/json/json-forwards.h (JOSNCPP)/jsoncpp.cpp –I(CURL)/include -lcurl

#include <curl/curl.h>
#include <json/json.h>
#include <iostream>
#include <string>
#include <memory.h>
#include <fstream>
#include <math.h>


#define LENFRAME  800

using namespace std;

size_t writeDataFunc(void *ptr, size_t size, size_t nmemb, string stream);
string base64_encode(unsigned char const* , unsigned int len);

static const string base64_chars =
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             "abcdefghijklmnopqrstuvwxyz"
             "0123456789+/";
int main() {
    char* openApiURL = (char*)"http://aiopen.etri.re.kr:8000/WiseASR/Recognition";
    char* audioFilePath = (char*)"AUDIO_FILE_PATH";
    string accessKey = "YOUR_ACCESS_KEY";
    string languageCode = "LANGUAGE_CODE";

    Json::Value request;
    Json::Value argument;
    FILE* fp = fopen(audioFilePath, "rt" );
    unsigned char sbuf[LENFRAME];
    unsigned int nRead = 0;
    unsigned int nWrite = 0;

    unsigned char* audioBytes;
    unsigned char* newAudioBytes;
    while ( 0 < ( nRead = fread( sbuf, sizeof( char ), LENFRAME, fp ) ) ) {
        newAudioBytes = new unsigned char[nWrite + nRead];
        if ( 0 < nWrite ) {
            memcpy(newAudioBytes, audioBytes, nWrite * sizeof(char));
        }
        memcpy(newAudioBytes + nWrite, sbuf, nRead * sizeof(char));
        nWrite = nWrite + nRead;
        audioBytes = new unsigned char[nWrite];
	    memcpy(audioBytes, newAudioBytes, nWrite * sizeof(char));
    }
    fclose(fp);

    argument["language_code"] = languageCode;
    argument["audio"] = base64_encode( audioBytes , nWrite);

    request["access_key"] = accessKey;
    request["argument"] = argument;

    CURL *curl;
    curl_slist* responseHeaders = NULL;
    curl = curl_easy_init();

    if( curl == NULL ) {
    } else {
        responseHeaders = curl_slist_append( responseHeaders , "Content-Type: application/json; charset=UTF-8" ) ;
        string requestJson = request.toStyledString();
        long statusCode;
        string response;

        curl_easy_setopt(curl, CURLOPT_URL, openApiURL);
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, responseHeaders ) ;
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5);
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, requestJson.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeDataFunc);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

        curl_easy_perform(curl);

        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
        curl_easy_cleanup(curl);

        cout << "[responseCode] " << statusCode << endl;
        cout << "[responBody]" << endl;
        cout << response << endl;
    }

    return 0;
}

size_t writeDataFunc(void *ptr, size_t size, size_t nmemb, string stream) {
    size_t realsize = size * nmemb;
    string temp(static_cast<const char*>(ptr), realsize);
    stream.append(temp);
    return realsize;
}

string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
  string ret;
  int i = 0;
  int j = 0;
  unsigned char char_array_3[3];
  unsigned char char_array_4[4];

  while (in_len--) {
    char_array_3[i++] = *(bytes_to_encode++);
    if (i == 3) {
        char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
        char_array_4[3] = char_array_3[2] & 0x3f;

        for(i = 0; (i <4) ; i++)
            ret += base64_chars[char_array_4[i]];
        i = 0;
    }
}

  if (i) {
    for(j = i; j < 3; j++)
        char_array_3[j] = '\0';

    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
    char_array_4[3] = char_array_3[2] & 0x3f;

    for (j = 0; (j < i + 1); j++)
        ret += base64_chars[char_array_4[j]];

    while((i++ < 3))
        ret += '=';
  }
  return ret;

}
                                        
Python

python 3.0을 기준으로 작성되었습니다.

HTTP 통신을 위해 urllib3 라이브러리를 사용하여 제공하고 있습니다. Python 3.0 이하의 버전에서 예제를 실행하기 위해서는 별도로 urllib3의 설치가 필요합니다. 설치에 대한 설명은 https://pypi.python.org/pypi/urllib3 를 참고하시기 바랍니다. urllib3 라이브러리에 대한 자세한 설명은 https://urllib3.readthedocs.io/en/latest/ 에서 확인 하실 수 있습니다.

#-*- coding:utf-8 -*-
import urllib3
import json
import base64
openApiURL = "http://aiopen.etri.re.kr:8000/WiseASR/Recognition"
accessKey = "YOUR_ACCESS_KEY"
audioFilePath = "AUDIO_FILE_PATH"
languageCode = "LANGUAGE_CODE"

file = open(audioFilePath, "rb")
audioContents = base64.b64encode(file.read()).decode("utf8")
file.close()

requestJson = {
    "access_key": accessKey,
    "argument": {
        "language_code": languageCode,
        "audio": audioContents
    }
}

http = urllib3.PoolManager()
response = http.request(
    "POST",
    openApiURL,
    headers={"Content-Type": "application/json; charset=UTF-8"},
    body=json.dumps(requestJson)
)

print("[responseCode] " + str(response.status))
print("[responBody]")
print(response.data)
                                       
Node.js
var fs = require('fs');
var openApiURL = 'http://aiopen.etri.re.kr:8000/WiseASR/Recognition';
var accessKey = 'YOUR_ACCESS_KEY';
var languageCode = 'LANGUAGE_CODE';
var audioFilePath = 'AUDIO_FILE_PATH';
var audioData;

var audioData = fs.readFileSync(audioFilePath);

var requestJson = {
    'access_key': access_key,
    'argument': {
        'language_code': languageCode,
        'audio': audioData.toString('base64')
    }
};

var request = require('request');
var options = {
    url: openApiURL,
    body: JSON.stringify(requestJson),
    headers: {'Content-Type':'application/json; charset=UTF-8'}
};
request.post(options, function (error, response, body) {
    console.log('responseCode = ' + response.statusCode);
    console.log('responseBody = ' + body);
});
                                        
음성인식 API 레퍼런스
요청 파라미터

음성인식 API에 필요한 요청 본문에 다음과 같은 파라미터를 작성해야 합니다.

[HTTP Request Body]
{
    “access_key”: “YOUR_ACCESS_KEY”,
    “argument”: {
        “language_code”: “LANGUAGE_CODE”,
        “audio”: “BASE64_OF_AUDIO_DATA”
    }
}
                                        

다음은 파라미터에 대한 설명입니다.

Field 명 타입 필수 여부 설명
access_key String O API 사용을 위해 ETRI에서 발급한 사용자 Access Key 입니다.
argument Object O API 사용 요청 시 분석을 위해 전달할 내용입니다.
language_code String O 음성인식의 입력 음성 언어 코드입니다. 요청할 수 있는 언어 코드는 아래와 같습니다.
  • korean : 한국어 음성인식 코드
  • english: 영어 음성인식 코드
audio String O 음성인식을 할 녹음된 음성파일의 base64-encoded string 입니다.
응답

음성인식 API는 요청된 어휘의 동음이의어 정보 결과를 JSON 형태의 Text 데이터로 반환합니다.

다음은 정상적인 요청 처리에 대한 HTTP 응답 예입니다.

[HTTP Response Header]
Access-Control-Allow-Origin:*
Connection:close
Content-Length:0
Content-Type:application/json; charset=UTF-8

[HTTP Response Body]
{
    "request_id": "reserved field",
    "result": 0,
    "return_object": {음성인식 결과 JSON}
}
                                        

다음은 오류가 발생한 요청 처리에 대한 HTTP 응답 예입니다.

[HTTP Response Header]
Access-Control-Allow-Origin:*
Connection:close
Content-Length:0
Content-Type:application/json; charset=UTF-8

[HTTP Response Body]
{
    "request_id": "reserved field",
    "result": -1,
    "reason": {오류 메시지}
}
                                        

분석된 결과는 다음과 같은 내용이 포함되어 있습니다.

JSON Key 이름 설명
recognized 음성 언어 코드에 따른 음성인식 결과
오류 코드

음성인식 API가 발생시킬 수 있는 오류코드는 다음과 같습니다.

http status code result reason 설명
400 -1 Required arguments is empty 필수 파라미터의 값이 없는 경우
400 -1 One or more arguments are not valid 파라미터의 값이 유효하지 않는 경우
413 -1 Request Entity Too Large 요청 문장 또는 어휘의 크기가 서버가 처리 할 수 있는 것보다 큰 경우
429 -1 Too Many Requests 사용자가 주어진 시간 내에 서버에 너무 많은 요청을 하는 경우
404 -1 Unknown Handler 등록되지 않는 서비스를 요청한 경우
408 -1 Handler Timeout 서버의 요청 대기가 시간을 초과한 경우
500 -1 ETRI service server connection refused ETRI 분석 서버에서 요청을 받지 못하는 경우
500 -1 ETRI service server is not exists 수행 가능한 ETRI 분석 서버가 없는 경우
500 -1 Recipient Failure ETRI 분석 서버에서 요청을 처리하지 못하는 경우
500 -1 Unknown ReplyFailure API 요청에 알 수 없는 내부 오류가 발생한 경우
500 -1 Unknown Exception 알 수 없는 내부 오류가 발생한 경우
※ 제약사항

본 음성인식 API는 상용서비스를 목표로 하지 않으므로 신규 또는 전문 어휘를 실시간으로 반영하지 않고 있습니다. 이에 따라 전문 분야의 어휘나 신조어 등 사전에 등록되지 않은 어휘 및 해당 어휘를 포함하는 문장에 대해서는 음성인식 성능이 저하될 수 있음을 양지하여 주시기 바랍니다. (본 음성인식 기술은 의사 형태소(pseudo morpheme)라는 기본 인식 단위를 사용함으로써 상용 서비스 적용 시, 어휘 적용범위(coverage)를 쉽게 제고할 수 있게 구현되어 있습니다.)