Бесплатное распознавание речи от российской компании Стэл

от автора

Когда возникает необходимость превратить звуковой файл с речью в текст, первыми на ум приходят решения Гугла и Яндекса. Но, кроме Яндекса, есть ещё одна отечественная компания — «Стэл» (http://speech.stel.ru/), API которой поддерживает «over 9000» и даже «очень очень много» запросов в день, а пробные ключи Stel раздает бесплатно.

image

Разобраться с API не так уж сложно, но на момент написания этой статьи мануал с сайта Стэла устарел и не работает, посему здесь будет представлен мануал с примерами на Python и Java. Пример на Java особенно актуален, если звуковой файл у вас имеется не в виде файла, а в виде массива байтов. Сразу стоит отметить, что Стэл работает только с wav-файлами с частотой дискретизации 8 кГц, размером сэмпла 16 бит, моно (один канал).

Ближе к делу: на сайте Стэла (http://speech.stel.ru/api_description) подробно описано, что и как (хоть на данный момент и немного устарело), посему, приводим сразу работающий (опять же, на данный момент) пример на питоне:

coding: utf-8 import httplib, json, base64  HOST = 'api.stel.ru:7071' APIKEY = '***' # Place your API key here MODEL = 'rus_gsm_ext'  WAV = base64.b64encode(open('test.wav', 'rb').read()) # demo audio file (WAV, 8000 HZ, 16-bit, mono) con = httplib.HTTPConnection(HOST)  #Speech recognition data = json.dumps({'apikey' : APIKEY, 'model': MODEL , 'wav' : WAV}) headers = {'Content-Type' : 'application/json', 'Accept': 'application/json', 'Content-Length' : '{0}'.format(len(data))} con.request('POST', '/kwfind', data, headers) resp = con.getresponse()  if resp.status == 200:   print json.loads(resp.read()) # UTF-8 string with recognized text else:   print resp.reason 

Как видно, тут на распознавание отправляется test.wav из рабочей директории скрипта. Аналогичный код на Java, а также код работающий с массивами байтов, приведены ниже. Во-первых, класс, который массивы байтов (без разметки) и файлы превращает в массивы байтов, соответствующие wav-файлу в указанном формате (нам будут нужны 8000 герц, 2 байта, 1 канал):

package ru.habrahabr.stel.example;  import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays;  import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.UnsupportedAudioFileException;  public class WaveFile { 	private int INT_SIZE = 4; 	public final int NOT_SPECIFIED = -1; 	private int sampleSize = NOT_SPECIFIED; 	private long framesCount = NOT_SPECIFIED; 	private byte[] data = null; 	private AudioInputStream ais = null; 	private AudioFormat af = null;  	WaveFile(File file) throws UnsupportedAudioFileException, IOException 	{ 		if(!file.exists()) 		{ 			throw new FileNotFoundException(file.getAbsolutePath()); 		} 		 		ais = AudioSystem.getAudioInputStream(file);  		af = ais.getFormat(); 		framesCount = ais.getFrameLength(); 		sampleSize = af.getSampleSizeInBits()/8; 		long dataLength = framesCount*af.getSampleSizeInBits()*af.getChannels()/8; 		data = new byte[(int) dataLength];  		ais.read(data); 	} 	 	WaveFile(int sampleSize, float sampleRate, int channels, int[] samples) throws Exception 	{ 		if(sampleSize < INT_SIZE) 		{ 			throw new Exception("sample size < int size"); 		} 		 		this.sampleSize = sampleSize; 		this.af = new AudioFormat(sampleRate, sampleSize*8, channels, true, false); 		this.data = new byte[samples.length*sampleSize]; 		 		for(int i=0; i < samples.length; i++) 		{ 			setSampleInt(i, samples[i]); 		} 		 		framesCount = data.length / (sampleSize*af.getChannels()); 		ais = new AudioInputStream(new ByteArrayInputStream(data), af, framesCount); 	} 	 	WaveFile(int sampleSize, float sampleRate, int channels, byte[] wave) throws Exception 	{ 		this.sampleSize = sampleSize; 		this.af = new AudioFormat(sampleRate, sampleSize*8, channels, true, false); 		this.data = Arrays.copyOf(wave, wave.length); 		 		framesCount = data.length / (sampleSize*af.getChannels()); 		ais = new AudioInputStream(new ByteArrayInputStream(data), af, framesCount); 	} 	 	public AudioFormat getAudioFormat() 	{ 		return af; 	} 	 	public byte[] getData() 	{ 		return Arrays.copyOf(data, data.length); 	} 	 	public byte[] getWave() throws Exception 	{ 		ByteArrayOutputStream bts = new ByteArrayOutputStream(); 		AudioSystem.write(new AudioInputStream(new ByteArrayInputStream(data),  				af, framesCount), AudioFileFormat.Type.WAVE, bts); 		return bts.toByteArray(); 	} 	 	public int getSampleSize() 	{ 		return sampleSize; 	} 	 	public double getDurationTime() 	{ 		return getFramesCount() / getAudioFormat().getFrameRate(); 	} 	 	public long getFramesCount() 	{ 		return framesCount; 	} 	 	public void saveFile(File file) throws IOException 	{ 		AudioSystem.write( new AudioInputStream(new ByteArrayInputStream(data),  				af, framesCount), AudioFileFormat.Type.WAVE, file); 	} 	 	public int getSampleInt(int sampleNumber) 	{ 		 		if(sampleNumber < 0 || sampleNumber >= data.length/sampleSize) 		{ 			throw new IllegalArgumentException( 					"sample number is can't be < 0 or >= data.length/" 							+ sampleSize); 		} 		 		byte[] sampleBytes = new byte[sampleSize]; 		 		for(int i=0; i < sampleSize; i++) 		{ 			sampleBytes[i] = data[sampleNumber * sampleSize + i]; 		} 		 		int sample = ByteBuffer.wrap(sampleBytes) 				.order(ByteOrder.LITTLE_ENDIAN).getInt(); 		 		return sample; 	} 	 	public void setSampleInt(int sampleNumber, int sampleValue) 	{ 		byte[] sampleBytes = ByteBuffer.allocate(sampleSize). 				order(ByteOrder.LITTLE_ENDIAN).putInt(sampleValue).array(); 		 		for(int i=0; i < sampleSize; i++) 		{ 			data[sampleNumber * sampleSize + i] = sampleBytes[i]; 		} 	} } 

Стоит отметить, что это немного дописанный класс, взятый с http://blog.eqlbin.ru/2011/02/wave-java.html. В общем-то все, что тут добавлено — это функция getWave(), возвращающая массив байтов, соответствующих файлу, построенному одним из конструкторов. А также конструктор, принимающий массив байтов обычного raw-файла. Отправлять Стэлу будем именно результат функции getWave(). Далее приведена функция, которая принимает WaveFile, открывает соединение со Стэлом, отправляет все что нужно, закрывает соединение и возвращает распознанную строку:

String getResponseOn(WaveFile wf) { 	String res = new String(); 	try 	{ 		byte[] wav = wf.getWave(); 		HttpConnection conn = new HttpConnection("api.stel.ru", 7071); 		conn.open(); 		HttpState state = new HttpState(); 		 		PostMethod post = new PostMethod(); 		 		JSONObject data = new JSONObject(); 		data.put("apikey", "***");	// Place your API key here 		data.put("model", "rus_gsm_ext"); 		data.put("wav", new String(Base64.encodeBase64(wav))); 		 		post.setPath("/kwfind"); 		post.setRequestHeader("Content-Type", "application/json"); 		post.setRequestHeader("Accept", "application/json"); 		post.setRequestHeader("Content-Length", ""+data.toJSONString().length()); 		post.setRequestEntity(new StringRequestEntity(data.toJSONString(), "application/json", null)); 		post.execute(state, conn); 		 		res = res + (String) ((JSONObject) new JSONParser().parse(post.getResponseBodyAsString())).get("text"); 		conn.close(); 	} 	catch(Exception e) 	{ 		res = null; 	} 	return res; } 

Не забудьте, что надо заменить "***" на ваш ключ, а также, что WaveFile для getResponseOn создается с параметрами (2, (float) 8000, 1, (byte[]) raw), например:

String res1 = getResponseOn(new WaveFile(2, (float) 8000.0, 1, sound)); String res2 = getResponseOn(new WaveFile(new File("test.waw"))); //demo audio file (WAV, 8000 HZ, 16-bit, mono) 

Кроме того, нужно отметить, что в getResponseOn(WaveFile wf) используется org.json.simple.JSONObject и org.json.simple.parser.JSONParser, которые зачастую приходится качать отдельно, например, отсюда: www.java2s.com/Code/Jar/j/Downloadjsonsimple111jar.htm

«Стэл» легко идут на контакт, так что, если вам нужны будут другие языки или языковые базы, с ними можно договориться.

Напомним, что наша команда занимается разработкой интеллектуального домашнего помощника Лекси. Лекси — настольное устройство с искусственным интеллектом и полностью голосовым интерфейсом для управления умным домом. Устройство может получать информацию в интернете, управлять бытовой техникой, сообщать новости из социальных сетей. Кстати, почитать об интересных размышлениях о будущем подобных домашних роботов вы можете в этой статье .

Технология распознавания речи, как вы уже могли догадаться, у нас от Стела. При этом распознавание речи происходит полностью на борту устройства (обзор нашей собственной электроники можно посмотреть тут). Это даем нам ряд преимуществ по сравнению с конкурентными аналогами, например, увеличение скорости выдачи пользователю ответа, отсутствие активационной фразы и возможность прохождения работы без интернета.

Следите на нашим проектом в социальных: Вконтакте и Фейсбуке.

image
Спасибо за внимание.

ссылка на оригинал статьи http://habrahabr.ru/post/260683/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *