OOpartsの画面をシャープ化するブックマークレット~SVGフィルタで要素にシャープネスをかける

美少女ゲームがストリーミング配信で遊べるの? 今だけ無料?(2020/4時点)やったー!
……でも動画エンコードのせいでテキストがぼやぼやで、ちょっと読みづらい…….恋羽ちゃんかわいい……

ということで、CSS3のSVGフィルタを使ってゲーム画面にシャープネス効果をかけるブックマークレットを自分用に作ったのであった。ブヒ

試してみる

※2020/4/25日時点&デスクトップ版Google Chromeのみで動作確認

1.下のコードをすべてコピーする(3回クリックで1行選択)
2.適当なブックマークを作り、編集してブックマークのURLに下記のコードをペースト
3.oopartsでゲームが開始された後、ブックマークをクリックするとゲームに微かにシャープ効果がかかる

javascript:(function(){if(document.domain=="oo.parts"){if(!document.getElementById('#__oopsharpen')){document.body.insertAdjacentHTML("beforeend",'<svg style="display:none" id="__oopsharpen"><filter id="__flt_sharpness"><feConvolveMatrix order="3 3" targetX="1" targetY="1" result="__rslt1" kernelMatrix="0 -1 0 -1 5 -1 0 -1 0" /><feComposite in="SourceGraphic" in2="__rslt1" operator="arithmetic" k1="0" k2=".7" k3=".3" k4="0"/></filter></svg>');}vtags=document.getElementsByTagName("video");for(i in vtags){vtags[i].style.filter="url('#__flt_sharpness')";}}})();

ゲームを開いた状態でアドレスバーにコピペしてEnterでも試すことができる。またシャープネスはエンコードのノイズも強調してしまうので、弱めにしている。

仕組み

SVGフィルタを使ってvideo要素にシャープネス処理を適用しているだけ。

SVGフィルタとは、SVGフォーマットの乗算やマスクなどの画像合成や、RGB色変換などの画像効果をCSSのフィルタから呼び出せてしまう、CSS3の機能だ。ブラウザがSVGのレンダリング処理もやっているなら、他からも使えた方が効率的なのは確かなんだろな。

SVGのフィルタや他の仕様については以下のサイト様が大変詳しく解説なされております。
http://defghi1977.html.xdomain.jp/tech/svgMemo/svgMemo.htm

詳しいことは、このサイト様やMDNを見てもらう方が遥かにわかりやすいと思うので、ここではSVGフィルタを使ったシャープネス処理のやり方をざっくりと書く。

まずはブックマークレットのHTML部分。javascriptはこれをドキュメント内にパッと書込んでるだけなんで割愛。

<svg style="display:none">
   <defs>
      <filter id="__flt_sharpness">
         <feConvolveMatrix
            result="__rslt1"
            order="3 3"
            targetX="1"
            targetY="1"
            kernelMatrix=" 0 -1  0
                          -1  5 -1
                           0 -1  0" />
         <feComposite
            in="SourceGraphic"
            in2="__rslt1"
            operator="arithmetic"
            k1="0"
            k2=".8"
            k3=".2"
            k4="0" />
      </filter>
   </defs>
</svg>

<video style="filter:url(#__flt_sharpness);">

body直下に<svg>タグでSVG要素を作成してfilterタグに呼び出し用のid名を指定。defsタグは省略できるが一応いれておくことにした。
シャープネスの処理を行っているのはfilterタグ以下の2つのタグ。

feConvolveMatrix
    order               [X<int> Y<int>]  行列範囲
feComposite

feConvolveMatrixは、ピクセルとその周辺ピクセルに行列内の係数をかけ合わせて増減させる。よくわからないと思うが、とりあえずこれでボカシ、エッジ抽出(ラプラシアンフィルタ)そしてシャープネスといった処理を実装することができる。

orderで効果範囲と行列サイズ(後述)を設定する。シャープネスの場合は周辺3x3ピクセルの範囲との差を増幅した処理を行っている。
これでシャープネス強度100%のバッキバキな画像ができあがる。
なのでfeCompositeでソース画像を合成して効果量を調整しよう。operator="arithmetic"を指定し、k2を入力画像in(ここではソース画像)の割合、k3をin2(ここではシャープ化画像)の割合として、k2 + k3 = 1.0となるようにする。この場合元画像0.8に対して0.2で合成している。Photoshopのアンシャープフィルタの効果量に換算すると25%くらいだろうか。
行列の中心の値を適当に上げることでもシャープの強度の調整ができる。feCompositeの処理が必要なくなるのでこちらの方が高速化できるだろう。


<filter id="__flt_sharpness_lite">
  <feConvolveMatrix
    order="3 3"
    targetX="1"
    targetY="1"
    divisor="5"
    kernelMatrix=" 0 -1  0
                  -1  9 -1
                   0 -1  0" />
</filter>

ここでは新たにdivisor="5"を指定して除算している。この時の行列内の各係数このようになる。

画像1

全て足し合わせると各係数の合計値は5/5=1となる。ただdivisorの指定を省略してしまっても1になるように自動で設定される。divisorの設定は合計値1以外の結果が必要な時にを設定するのが想定なんだろうか?
ConvolveMatrixのアルゴリズムを調べるとこの分数表記が出てくるので頭の片隅にとどめておこう。

アンシャープマスク

なお、画像縮小には欠かせないアンシャープマスクもSVGフィルタで実現できる。

<svg style="display:none">
   <defs>
      <filter id="__flt_unsharpmask">
         <feGaussianBlur
            result="__rslt1"
            stdDeviation="3 3" />
         <feComposite
            in="SourceGraphic"
            in2="__rslt1"
            operator="arithmetic"
            k1="0"
            k2="1.2"
            k3="-0.2"
            k4="0" />
      </filter>
   </defs>
</svg>

<video style="filter:url(#__flt_unsharpmask);">

アンシャープマスクの動作は、元画像からぼかし後の画像を引いてできた差分を、元画像に足せば潰れる部分が強調される――という感じ。直感的にはわかりづらいけども。 

この方法では、Photoshopのアンシャープマスクでいうところの半径と適用量を設定できる。閾値は実装してない。色分割を使えばできるが処理量は倍くらいになる。

feGaussianBlurは組み込みのガウシアンフィルタ。feConvolveMatrixでやるよりも高速で範囲の指定も融通が利く。stdDeviationがアンシャープの半径XYとなる(chromeに限り偶数値、小数点値も可)
feCompositeは強度の調整だが、今回は元画像(k2)とぼかし画像(k3)の差分をとることも併せて行っている。アンシャープマスクの式は”元画像+(元画像-ガウス)”なので、これを展開すると元画像の係数を1以上にして、ガウスぼかし後画像を負の値にすることで同じことができる。
とまあそんなこんなで、k2, k3の値は以下の通りになる。

k2 >1.0
k3< 0
k2 + k3 = 1.0

数値で示すとk2="1.5" k3="-0.5"とか k2="2.45" k3="-1.45"とかそんな感じ。

実行速度

ほぼ画面全体に毎秒60回以上でシャープネスをかけ続けるなんて、CPUで処理しきれるか怪しい処理だ。でもいまはWebGLの時代なので6年落ちの低スペック機でも特に重さを感じさせずに動いた。
フィルタを5個6個重ねてみると動画のコマ落ちが発生してくる。なのにCPU&GPU使用率はほとんど上がってなかったりする謎。

SVGフィルタ便利なのでは??

今回は特定のサイト用にしか使っていないが、画像を縮小してシャープネスやアンシャープマスクをかけるのは、頻繁に使う画像補正なので使える場面はかなり多いはずだ。

話をエロゲ寄りに戻せば、エロゲ絵はくっきりとした絵なので拡大縮小でディテールが潰れやすい。なので拡大縮小が前提のレスポンシブなWEBサイトでは絵の品質を伝えづらいという問題があった。S○NY的な言葉でいうなら感動度が低かった。
何サイズか出力しておいて、その間をクロップ範囲で微調整して合わせるといった対策もある。とはいえその出力もPhotoshopで元サイズから拡大縮小して適切な量のシャープネス・アンシャープマスクをかけるんだから、ページ上で拡縮にあわせて効果をかけられるのが一番手っ取り早かった。

SVGフィルタによるシャープネスやアンシャープマスクは有用なんじゃないでしょうか?

てことで、おわり。

メモ

firefoxで画像が表示されない
<svg sytle="display:none">だとfirefoxはSVG要素をレンダリングしない。なので以降の<filter>も読んでくれないし、さらにフィルタが適用された画像のレンダリングにも失敗する――という感じのようだ。
なので"display:none"ではなく、"visibility:hidden;width:0;height:0;"とかで消す。ボックスが残っていても問題なければ要らないけども。
また、外部SVGファイルからフィルタを呼び出せる filter:url("./file.svg#filter-id");

旧Microsoft Edge(EdgeHTML 18)で表示されない
filter:url()で呼び出すのは無理ぽい。SVG要素内ならSVGフィルタものは実装されてる。


この記事が気に入ったらサポートをしてみませんか?