스피커 - 작동 방식

Speakr은 모바일을 펜처럼 사용하여 허공에 글을 쓸 수 있는 웹 앱입니다. 이러한 경로를 이미지의 문자로 렌더링하기 전에 온보드 IMU를 사용하여 움직임을 기록합니다. 그런 다음 필기 인식을 사용하여 작성된 텍스트를 결정한 다음 텍스트 음성 변환을 통해 소리내어 재생합니다.

대안적인 상호 작용 모드를 위한 실험으로 Speakr를 만들었습니다. 이 프로젝트는 DigitalOcean App Platform Hackathon과 잘 연결되어 있습니다.

그렇다면 스피커는 무엇입니까? Speakr는 다양한 시나리오에 적용할 수 있는 새로운 형태의 커뮤니케이션을 보여줍니다. 아마도 더 나은 상호 작용을 위해 타사 모바일 게임의 일부로 사용할 수 있습니다. 이 설정을 사용하면 사용자 지정 ML 모델로 화살표 및 도형과 같은 제스처를 인식하여 스마트 홈 장치를 보다 직관적으로 제어할 수 있습니다. 텍스트 또는 음성 번역에 대한 제스처로 제공되는 가능성은 정말 끝이 없습니다.

비디오에 표시된 Speakr 웹 앱용code은 다음과 같습니다. 이 웹 앱은 React, NodeJS 및 Google Cloud API로 구축되었으며 서버는 DO 앱 플랫폼에서 호스팅됩니다. 아래에서는 이미지 기능에 대한 제스처의 주요 부분을 살펴보겠습니다.

작동 방식



아래는 내 휴대폰의 IMU에 기록된 움직임에서 렌더링된 그리기 기능의 초기 테스트입니다. 꽤 좋아!


아래에 간단한 콜백으로 표시된 센서 데이터를 읽기 위해 Generic Sensor API을 사용하기로 했습니다.

this.sensor = new AbsoluteOrientationSensor({
      frequency: 60,
});

this.sensor.addEventListener("reading", (e) => this.readSensor(e));



처음에는 가속도계로 시작했지만 너무 시끄럽고 신뢰할 수 없었습니다. 방향 융합 센서로 빠르게 전환했습니다.

여러 실제 센서의 데이터를 결합하여 사용하기 쉽도록 데이터를 결합하고 필터링하는 새로운 가상 센서를 구현할 수 있습니다. 이러한 센서를 융합 센서라고 합니다. 이 경우 온보드 자력계, 가속도계 및 자이로스코프의 데이터가 AbsoluteOrientationSensor 구현에 사용됩니다.

이동 거리를 계산한 후 이동 경로는 각 문자에 대한 모든 이전 경로를 저장하는 배열로 푸시됩니다.

readSensor(e) {
    let q = e.target.quaternion;
    let angles = this.toEuler(q);

    if (!this.draw) {
      this.initAngle = angles;
      this.draw = true;
    }

    let pos = angles.map((angle, i) => this.calcDist(angle, i));
    this.text[this.numChar][0].push(pos[0]);
    this.text[this.numChar][1].push(pos[1]);
  }



센서 API가 반환되었으므로quaternions 더 쉽게 조작할 수 있도록 오일러 각도로 변환해야 했습니다. 다음은 Javascript의 기본 구현입니다. 2차원으로만 작업하기 때문에 피치는 생략되었습니다.

// Wikipedia Implementation
  toEuler(q) {
    let sinr_cosp = 2 * (q[3] * q[0] + q[1] * q[2]);
    let cosr_cosp = 1 - 2 * (q[0] * q[0] + q[1] * q[1]);
    let roll = Math.atan2(sinr_cosp, cosr_cosp);

    let siny_cosp = 2 * (q[3] * q[2] + q[0] * q[1]);
    let cosy_cosp = 1 - 2 * (q[1] * q[1] + q[2] * q[2]);
    let yaw = Math.atan2(siny_cosp, cosy_cosp);
    return [yaw, roll];
  }



투영된 초기 각도와 현재 각도 사이의 거리는 '그리기' 버튼을 누른 후 초기 각도 위치가 휴대폰의 시작 방향으로 설정된 상태에서 간단한 삼각법과 각도 차이를 사용하여 계산됩니다.

calcDist(angle, i) {
    angle = (angle - this.initAngle[i]) * (180 / Math.PI);
    angle = angle < 0 ? angle + 360 : angle;
    angle = angle > 180 ? angle - 360 : angle;
    let dist = -1 * Math.tan(angle * (Math.PI / 180));
    return dist;
  }



마지막으로 이미지를 생성하려면 저장된 움직임을 캔버스에 렌더링해야 합니다. 스케일링과 오프셋의 조합으로 각 '글자'는 테두리가 있는 상자로 크기가 조정되고 다른 모든 글자와 결합되어 단어가 공중에 그려진 이미지를 형성합니다.

renderText(ctx) {
    ctx.beginPath();

    this.text.forEach((char, i) => {
      let xpos = char[0];
      let ypos = char[1];

      let xmin = Math.min(...xpos);
      let ymin = Math.min(...ypos);

      if (xmin > 0) xmin = 0;
      if (ymin > 0) ymin = 0;

      let xrange = Math.max(...xpos) - xmin;
      let yrange = Math.max(...ypos) - ymin;

      let xmulti = (this.letterSize * this.letterWidth) / xrange;
      let ymulti = (this.letterSize * this.letterHeight) / yrange;
      let multi = Math.min(xmulti, ymulti);

      let xoffset = this.border + (this.letterWidth - xrange * multi) / 2;
      let yoffset = this.border + (this.letterHeight - yrange * multi) / 2;

      let letterOffset = i * this.letterWidth;

      for (let j = 0; j < xpos.length; j++) {
        let x = xoffset + (Math.abs(xmin) + xpos[j]) * multi + letterOffset;
        let y = yoffset + (Math.abs(ymin) + ypos[j]) * multi;

        if (j === 0) {
          ctx.moveTo(x, y);
        } else {
          ctx.lineTo(x, y);
        }
      }
    });

    ctx.stroke();
  }



그런 다음 캔버스 요소에서 이미지가 생성됩니다. 다음은 문자 크기, 여백, 선 너비 등과 같은 렌더링의 다양한 속성을 조정하는 반복의 몇 가지 예입니다.



Google Vision API가 작동하려면 충분히 읽을 수 있어야 하고 필기 텍스트를 결정하기 위해 기본적으로 필기 인식을 수행해야 하므로 이것이 문제의 대부분이었습니다. 이것은 주로 시행 착오였으며 허용 가능한 수준의 정확도에 도달하는 데 상당한 시간이 걸렸습니다.

마지막으로 이 텍스트는 Google TTS API를 사용하여 음성으로 변환되고 휴대폰에서 음성으로 전달됩니다.

Speakr을 마음껏 가지고 놀아보세요!

좋은 웹페이지 즐겨찾기