“HTML Speech Input” on Google Chrome

先日、Google Chromeがバージョンアップしてv11になり、音声入力APIがブラウザで実装されましたね。iPhoneなどのスマートフォンではすでにお馴染みの機能ですが、やっとこさ、PCバージョンのブラウザでも搭載されました。

なんか難しそうに思いますが、既存のHTMLのINPUT タグに、x-webkit-speech 属性を追加するだけ。

まだ独自仕様なので、speech 属性ではなく、x-webkit-speech となっています。Google Translater(翻訳) なんかで確認すると、speech と x-webkit-speech の二つが指定されています。いずれ標準化されるんでしょうかね。
lang属性で、言語を指定できるみたいです。指定しない場合はOSの言語設定が適用されるみたいで、日本語もちゃんと認識されます・・・が、しゃべる人によって認識率は低くなるのは、スマートフォンの場合と同様のようです(笑)
カタカナ語をしゃべると、英語だったり、日本語だったり、結構揺れますね~( ̄0 ̄)

HTML5仕様のようですが、別にHTML4で書いても認識出来るみたいで(当たり前か(^^;;)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ja">
	<head>
		<meta http-equiv="content-type" content="text/html; charset=shift_jis">
		<title>Title...</title>
	</head>
	<body>
    <input type="text" name="q" speech x-webkit-speech>
	</body>
</html>

で、表示はお馴染みのマイクマークが付きます。

htmlspeech

で、音声入力をすると、この入力ボックスにワード(語)が入るので、DOM操作でこのワードを拾うときは、onwebkitspeechchange イベントをハンドリングすればいいようです。
HTML5仕様的には、onspeechchangeなんでしょうけど・・・、speech属性と同じく、onspeechchangeはまだ認識されませんでした。

// <input type="text" name="q" x-webkit-speech onwebkitspeechchange="SpeechChange(this)">
function SpeechChange(oInput)
{
  //alert(this.value); ← 記述ミス(間違い)修正しました
  alert(oInput.value);
}


//HTMLにonxxxxとか書きたくない場合は・・・、
// <input type="text" name="q" x-webkit-speech>
var oInputs =  document.getElementsByName('q');
for(var i in oInputs)
{
  if(typeof oInputs[i] == 'object')
    oInputs[i].addEventListener('webkitspeechchange',function(){alert(this.value);});
}
//とかでしょうか。

ここでややこしいのは、onchangeイベントでは拾えない、っちゅーこと。onchange は音声入力以外での入力に反応するようです。ややこしい。いずれはonchangeで拾えるようになるんじゃないかな~、とは思っているんですが・・・どうなんでしょうかね。イベントを別々にする意味はあまりないような気がするんですが・・・。

それと・・・まだ、エラーイベント(onwebkitspeecherror)がまだ実装されていないみたいすね~。
他のブラウザも実装してくれるんでしょうかねぇ・・・?

既存の入力ボックスに、x-webkit-speech 属性を追加するだけでいいので手間がかかりませんし(文字列置換で済むと思う)、問い合わせページとかの場合は、speech 属性とともに付けとくと、アクセシビリティーの向上?にはいいかもしれません。とはいいつつ、音声入力を必要とするユーザーのchrome使っている比率はメッチャ低いと思いますが・・・ただそう感じただけで根拠はありません(^^;;;

混ぜると危険

どーでもいい話です(^^ゞ

str()という関数があらかじめ定義されているとして・・・下のコードは何?(コードに特別な意味はありません)

function fx($p,$d)
{
  $a = $p.str(1);
  $b = $d.str(2);

  return $a * $b;
}

「これは、phpの関数だ」と思った方と、
「これは、Javascriptの関数オブジェクトだ」と思った方もいる。

最近、DOM+javascriptでjQueryライブラリを本格的に使い出しまして・・・jQueryを駆使した、あの特徴のあるコードを書いていると、何か変な気分になる。

もちろんjavascriptでは、$ マークに特別な意味はない。だから普通に変数名に使える。
jQueryのショートカットとしてよく使う。

一方PHPでは、変数を表す予約文字となってる。

また、ピリオド文字は、PHPでは文字列連結演算子で、javascriptではドット演算子・・・オブジェクトのプロパティへアクセスするための演算子。

う~ん・・・なんだか、気色悪い・・・

こんなしょーもないことを思うのは、ちょっと疲れてんのかな~・・・。
PHPとJavascript+jQuery を混ぜたコードを書く場合(そんなケースは希だけど)、地獄を見る気がしてならない(^^;;;

・・・ちょっと長めのつぶやきでした(^^;;;

ダミーテキスト

Webページのレイアウトを考えるとき、FireWorksでページのレイアウトデザインを作成して、それを元にパーツ画像・ボタン類などを作成、あとはDreamWeaverなり、テキストエディタなりでHTMLとCSSを組んでいく・・・という流れがごくごく一般的だと思います。
 

デザインだけで内容が全く決まっていない、というとき、テキスト部分には、たとえば、「ダミーテキスト」などの仮のテキストの羅列を入れることが多いのですが、やはり同じ内容のテキストの羅列だと、完成したときのイメージが湧かない、と言われたこともありました。
 

FireWorksで、ダミーテキスト生成して挿入する下のようなコマンドを作成して、%APPDATA%\Adobe\FireWorks CS4\Commands へ放り込んで使っています。やっていることは至極単純で、テキストファイルの各行を配列に読み込んで、乱数で得られた数字を使って数回、配列から読み出しているだけ。
 

問題は、それに使用するテキストファイルの内容。ある程度日本語として文法が正しく、かつ、それ文章自体は意味をなさない・・・という、とにかく訳のわからないテキストが理想です(笑)
ネットから勝手に取って使用すると「著作権が・・・」とかあると思うので、パブリックドメインのテキストがないか調べて、僕は3つのテキストを選択しました。パブリックドメインなので、どう使おうと自由、という非常にありがたいものです。。。
 

僕が選んだのは、聖書の英語訳と日本語訳。それと、平家物語の原文。もちろん全文読み込んだりすると処理に非常に時間がかかるので適当に抜き出し、加工を加えました。

ダミーテキストのソースファイル

でも、これだけだと不満があって、英文と日本文が適度に混じったテキストがないか探索中です。手っ取り早く、技術文書などの専門書から取るのがいいのですが・・・なかなかパブリックドメインのものがねぇ~、ないんですよねぇ~。
 

ダミーテキストとしての僕の理想は、日本語と英語が適度に混ざって、かつ、カタカナもある程度混じって、なおかつ、意味が分からないテキスト(笑) そんなのないですかねぇ~・・・。
 

/************************************************************************
 ダミーテキスト挿入.jsf for FireWorks CS4
 %APPDATA%\Adobe\FireWorks CS4\Commands へ入れとく。
 
 ファイルが本当にテキストファイルかどうかのチェックはしていない。
*************************************************************************/
var num   = 5;
var times = 1;

insertDummyText = function(n,t)
{
  var file = fw.browseForFileURL("open","ダミーテキストファイルを開く");

  if(file == null)
	  throw new Error("ファイルが指定されていません");

	var fin = Files.open(file);
  var lines = [];
  var line_ = null;
  var out = [];

  while((line_ = fin.readline()) != null)
    {
      if(line_.match(/^\s*$/))
        continue;

      lines.push(line_);
    }

  fin.close();

  for(var i = 0;i < t;i++)
    {
      for(var j = 0;j < n;j++)
        {
          var index = parseInt(Math.random() * lines.length);
          out.push(lines[index]);
        }
      if(i < t - 1)
        out.push("\n\n");
    }

  var dom = fw.getDocumentDOM();

  dom.setTextAutoExpand(false);
  
  if(typeof fw.selection[0] == "undefined")
    dom.addNewText({top:5,right:dom.width/2,bottom:120,left: 5},true);

  var runs = fw.selection[0].textRuns;

  runs.textRuns = [{changedAttrs: {}, characters: out.join('')}];
  fw.selection[0].textRuns = runs;
}

try{insertDummyText(num,times);}catch(e){alert(e.message);}

GDで角丸サムネール画像の生成~その2~

GDで角丸サムネール画像の生成の続きです。
 
 

前回のコードだと、円の方程式から得られた判定に対して単に背景色の点を打つ、打たないだけだったので、角丸部分がガタガタになってしまい、非常に見苦しいものでした。それを隠すために(笑)元画像にガタガタの角丸を適用して元画像をリサンプルして誤魔化していました。
 

それでは、あまりにも情けない・・・ということで、アンチエイリアス処理を施そう・・・ということで、アンチエイリアスのアルゴリズムをネットで検索して調べましたが・・・アルゴリズム自体の解説が少なく、ドキュメントを見つけても、高校・大学時代にやったような、複雑な数式のオンパレードで・・・頭がクラクラ(ーー;;;
 

目眩を起こしつつザラッと流し読みしたけど・・・ドキュメントが英語なので・・・ほとんど理解できん・・・ああ、柔らか頭に戻りたい。。。
 

前置きが長くなりましたが・・・これは、もう、思いつきで行きあたりバッタリで実装するしかない!というわけで・・・
 

「なんちゃってアンチエイリアス処理」を書いてみました。
あくまで、なんちゃってなので、ガタガタより、まぁマシかな・・・というレベルです。
 

角丸

角丸部分にする上下左右の半径Rの1/4円弧を囲んだ正方形部分のピクセルをおのおのスキャンして円の方程式によりピクセル色を決定するところまでは前回と同じです。
変えたところは、判定部分で、ピクセル座標(x,y)の点において、xのときの円周上の点を求め、それとの差(delta)が、0 ≦ delta ≦ 1のとき、
若干薄く描画。その差が1以上のとき、0以下のときで、点を打つ・何もしないを決定します。実際には、↓の setPixelサブルーチンのようにしています。
忠実にアンチエイリアス処理をしている訳ではなく、0 ≦ delta ≦ 1 部分の描画色(描画色というか、背景色の透明度(α)を単に変化させています)の決定は、かなり適当です。「なんちゃってアンチエイリアス」たる所以です(^^;;;

#
# perl script
#

#
#簡易アンチエイリアス処理をする点(ピクセル)の描画サブルーチン
#
#$thisはGD::Imageを継承したオブジェクト,$rは角丸の半径ピクセル、$x,$yはピクセル位置、
#$yfは$xのときの円周上の実際のy座標(実数でピクセル位置ではない),
#$colorは背景色,$bは角丸が画像の上部分(1)か、下部分(0)か。

our $PARAM = 0.75;
our $RANGE = 127;
our $NEAREST_INNER = 120;
our $NEAREST_OUTER = 15;

sub setPixel
{
  my ($this,$r,$x,$y,$yf,$color,$b) = @_;
  my $retVal = 1;
  my $delta = ($b ? $yf - $y : $y - $yf) * ($b ? $r - $y : $r - $this->height + $y) / ($r * $PARAM);
  
  if($delta >= 0 && $delta <= 1)
    {
      $delta = 0.1 if($delta == 0);
      #ほぼ円周上
      my $range = $RANGE - round($delta*$RANGE);
      my $alphacolor = $this->colorAllocateAlpha($this->rgb($color),$range);

      $this->GD::Image::setPixel($x,$y,$alphacolor);
    }
  elsif($delta > 1)
    {
      #円外
      $delta < 2 ? $this->GD::Image::setPixel($x,$y,$this->colorAllocateAlpha($this->rgb($color),$NEAREST_OUTER)) : $this->GD::Image::setPixel($x,$y,$color);
    }
  elsif($delta < 0)
    {
      #円内
      $this->GD::Image::setPixel($x,$y,$this->colorAllocateAlpha($this->rgb($color),$NEAREST_INNER)) if($delta > -1);
      $retVal = 0;
    }

  $retVal;
}

#
#角丸部分のピクセル値をスキャンする。実際の描画は、上記のsetPixelを使う。
#
# $this は、GD::Imageを継承したオブジェクト 、$radiusは円弧の半径ピクセル
sub applyRoundCorner
{
  my ($this,$radius,$color) = @_;
  my ($x,$y) = (0,0);
  my ($width,$height) = ($this->width-1,$this->height-1);
  my $diameter = 2 * $radius;
  my $limit = $radius;
  $this->alphaBlending(1);
  
  if($diameter > $width || $diameter > $height)
    {
      print "Radius of round corner must be less than width or height...\n";
      print "Process can not apply round corner...\n";
      return;
    }

  #画像の上部分の角丸
  for($y = 0;$y < $limit;$y++)
    {
      #左
      for($x = 0;$x <= $radius;$x++)
        {
          my $yf = abs(sqrt(abs($radius**2 - ($x - $radius)**2)) - $radius);
          last unless($this->setPixel($radius,$x,$y,$yf,$color,1));
        }
      #右
      for($x = $width;$x >= $width - $radius;$x--)
        {
          my $yf = abs(sqrt(abs($radius**2 - ($x - ($width - $radius))**2)) - $radius);
          last unless($this->setPixel($radius,$x,$y,$yf,$color,1));
        }
    }

  #画像の下部分の角丸
  $limit = $height + 1 - $radius;
  for($y = $height;$y > $limit;$y--)
    {
      #左
      for($x = 0;$x <= $radius;$x++)
        {
          my $yf = abs(sqrt($radius**2 - ($x - $radius)**2) + ($height - $radius));
          last unless($this->setPixel($radius,$x,$y,$yf,$color));
        }
      #右
      for($x = $width;$x >= $width - $radius;$x--)
        {
          my $yf = abs(sqrt($radius**2 - ($x - ($width - $radius))**2) + ($height - $radius));
          last unless($this->setPixel($radius,$x,$y,$yf,$color));
        }
    }
}

というわけで、自作のサムネール作成 perlモジュールに組み込んで、実際に角丸サムネールを作成してみました。
  

拡大すると・・・

こんな感じ。
アンチエイリアスしている風に見えるでしょう。。。あああ。

アンチエイリアスがかかった角丸が簡単にできるライブラリ・・・ないもんでしょうかねぇ・・・。
 

忘れてたけどImagickってのもありましたねぇ~、多機能すぎて、一度挫折した覚えが・・・。

GDで角丸サムネール画像の生成

公開・表示したい画像がいっぱいあるとき、サムネール画像を作るのは非常に手間です。
いちいち一つ一つ画像編集する人は・・・まぁ、いないでしょうね・・・(^^;

以前に、CGIでも使えるようにGDを使用してサムネール画像生成のPerlモジュールを作成していました。

で、今回、そのサムネール画像に角丸を適用したものを生成できるように、機能を付け足すことにしたんですが・・・、
画像に角丸を適用するスマートな方法がわからず、考えたあげく、少々チカラワザ的にインプリメントしてしまいました。

四隅のピクセル値をスキャンして、円弧外のピクセル値は背景色に、円弧内のピクセル値はそのままに・・・という感じです。

ピクセル値の判定には、円の方程式、X2 + Y2 = Radius2 を使用しています。

#
# perl script
#
# $this は、GD::Image 、$radiusは円弧の半径ピクセル
sub applyRoundCorner
{
  my ($this,$radius,$color) = @_;
  my ($x,$y) = (0,0);
  my ($width,$height) = ($this->width-1,$this->height-1);
  $this->alphaBlending(1);
  my ($lhs,$rhs) = (0,$radius**2);

  #上
  for($y = 0;$y <= $radius;$y++)
    {
      #左
      for($x = 0;$x <= $radius;$x++)
        {
          $lhs = ($x - $radius)**2 + ($y - $radius)**2;
          last unless(setPixel($this,$radius,$x,$y,$color,$lhs,$rhs));
        }
      #右
      for($x = $width;$x >= $width - $radius;$x--)
        {
          $lhs = ($x - ($width - $radius))**2 + ($y - $radius)**2;
          last unless(setPixel($this,$radius,$x,$y,$color,$lhs,$rhs));
        }
    }

  #下
  for($y = $height;$y >= $height-$radius;$y--)
    {
      #左
      for($x = 0;$x <= $radius;$x++)
        {
          $lhs = ($x - $radius)**2 + ($y - ($height - $radius))**2;
          last unless(setPixel($this,$radius,$x,$y,$color,$lhs,$rhs));
        }
      #右
      for($x = $width;$x >= $width - $radius;$x--)
        {
          $lhs = ($x - ($width - $radius))**2 + ($y - ($height - $radius))**2;
          last unless(setPixel($this,$radius,$x,$y,$color,$lhs,$rhs));
        }
    }
}

#
# ピクセル(x,y)にカラー値をセットする。
#
sub setPixel
{
  my ($this,$radius,$x,$y,$color,$lhs,$rhs) = @_;
  my $ret = 1;
 
  if($lhs > $rhs)
    {
      $this->setPixel($x,$y,$color);
    }
  else
    {
      $ret = 0;
    }

  return $ret;
}

ただ、サムネール画像にこれをそのまま適用すると、当然ですがアンチエリアス処理がされないので角丸部分がガタガタになってしまいます。
そこで、サムネール画像に角丸を適用するのではなく、メモリ上の元画像に角丸を適用し、GD::ImageのcopyResampledでサムネール画像を生成(縮小)することでアンチエリアス処理の替わりとすることにしました。

他の方法としては、角丸部分をアルファ付きのPNGファイルを用いて、四隅にコピーするという方法があって、そっちの方が簡単かな? とは思うのですが・・・

根本的に改善するには、やはり画像処理のアルゴリズム(主にアンチエリアス処理なんですが・・・)について勉強しなければいけませんねぇ・・・。

他の方が作ったライブラリを探した方がいいかも・・・(^^;;;