やあ子供たち。この度の新型コロナウィルス対応で、これまで頑なに拒絶し続けてきた在宅ワークをおじさんも強いられることになったよ。しかしながら、突如として壁にぶちあたったのさ。会社貸与のパソコンを家に持ち帰って自宅のLANでVPNに入ろうとしたところ、どうもうまくいかないなと。
で、会社の担当様に問い合わせてみると、
「はぁー?古い規格のルータだったらだめだよ。」
「いや4年前に買ったやつでそーんなに古いものではー(汗)」(←微妙)
「あ、あるいはたぶん無線LANの暗号化方式をWPA2ってのにしてないからなのでは」
と言われたので、家の無線LANルータの設定を確認してみると、「無線設定」の中の暗号化方式として、
「WPA/WPA2 mixed何ちゃら」というオプションと、
「WPA何ちゃら」というオプションの2択しかない!
「WPA2何ちゃら」というオプションがなぜ出てこないのぢゃー!
(何故かゲストポートの中では「WPA2何ちゃら」というオプションが存在し、選択できて実際会社のVPNつながりもする。しかしこれをやっている間は他のゲストポートでないポートを使用している無線端末が軒並み使えなくなった。(これも仕様?)
型番は、BuffaloのWHR-1166DHP3という、2016年9月に買ったやつなのですが、とくに規格で目おとりするようなところは、、とくにWPA2もしっかり対応と製品情報見る限りは書いてあるし。。
で、もうこんな状況どう検索してよいやらさっぱり解決のとっかかりすらつかめない気がしたので、ここはサクッとBuffaloさんサポートに聞いてみたところ、結論としては、AOSSが有効になっていたからですと。無線設定の中のAOSS設定画面の一番上のとこでAOSSを無効にしてみましょうと。
→はい、問題は完全に解消しました。そしてこのAOSSが有効になってると暗号化方式としてWPA2が使えないのは(この機種の?)仕様だそうでした(すみません私には限られた時間の中見つけられなかったです)。
AOSSって、ボタン押すだけで接続設定できて便利なのかも知れないが、今回のことで二度と使うか、今後は手動設定でいくぜと思ったわまじで。ちなみに、AOSS無効にすると、何やらパスワードが一つ前のものに戻ってしまったのだけど、これもまた仕様なのだそうです。(注意!)
んん?何でしょうかこの超重要なことなはずなのだけどわかりやすく語られていない地雷仕様の数々。まサポートさんの対応もとてもよかったし、解決したからよかったのですけど。結果製品自体はしっかり動いてくれてるので文句はないんですけどね。(LINEサポート、まじ最高す。)
以上、台数たくさん売れてそうな機種のくせにググっても絶対わからなさそうな難易度の高い内容だったので、メモとして残しました。同じ境遇にはまった人の時間短縮になれば幸いです。
チャオ!
(なお、本記事を見て何か設定変更をして問題が発生しても何も責任はとれませんのでそこは自己責任でお願いします。上記は単に私の今日の思い出を綴っただけのものなので、原則としてメーカーのサポートさんにまずはご連絡されることをお勧めします。)
NodeJSだけでページ遷移(リダイレクト)(副題:「res.end();を忘れるな」)
やあみんな。
おじさんは最近NodeJSで遊んだりしているわけだけれども、
あるとき、ま普通にというか当然のように、ページ遷移(リダイレクト)をしたくなった。
がしかし、どうもExpressを使ったやり方しかなかなか見つけられず、いやそうじゃなくて、Expressを使わずして、それでもNodeJSの標準機能だけで実現する方法がどうしても知りたかったんだ。
ずばり、それは以下のようにするとできるようだ。(Chromeのみで確認済み)
server.on('request', function (req, res) { if (req.method === 'POST') { //POSTデータを受けとる req.on('data', function (chunk) { data += chunk }) .on('end', function () { // POSTされたデータの処理 console.log(data); // 表示確認してみたり、cookieに何か仕込んでみたり。。 // でここがページ遷移(リダイレクト)処理 res.writeHead(301, { Location: "/main.htm" });// ●ここな res.end();// ●ここだよ }); } }
はい、いかがだろうか。
これは、クライアントブラウザ側で、POSTされたデータを何か処理した後に、別のページへ、ページ遷移(リダイレクト)するための処理をイメージした内容だよ。
「●ここな」「●ここだよ」という2行が、今回紹介したい内容だ。
この2行が、このサーバースクリプト内で、動作定義されている、「/main.htm」というURLにページ遷移をしてくれるというわけさ。
ここでおじさんが伝えたかったことは、おじさんは世の解説ページにある、「●ここな」となっている、つまり1行目のみを記述して試したりしていたのだけど、さっぱりページ遷移は起こらずに、あきらめてExpressで作り直すかとしたときに、最後にもう一度だけという感じで、2行目の、res.end();も書くようにしてみたところ、やっと晴れてめでたくページ遷移させることに成功したというわけだった。なので、上記2行を記述することが重要なんですよということが伝えたかった。
なお、上記、「/main.htm」を、「https://nurs.hatenablog.com/」といった、一般的なURLとして指定することも可能だ。ちゃんとしっかりページ遷移(リダイレクト)するぞ。
というかだな、res.end();を忘れるな、だ。
res.end();を忘れてページがいつまでも遷移しなくて悩むのは時間の無駄だ!もったいないよ!
繰り返す。res.end();を忘れるな。
エウロパには近づくな。res.end();だけは、忘れるな。
チャオ!
古いiPadに古いバージョンのYouTubeをインストールする方法
やあみんな。
おじさんの持っている古いiPadでは、アプリストアから普通にYouTubeをダウンロードしようとすると、このアプリはお使いの古いiPadには対応してないのでダウンロードできませんと言われてしまうことを見つけたよ。
こんなことがあると、「もう古いからYouTubeも入れられなくなってしまったんだ。」「この古いiPadを使い続けようとするのは、サポート切れを迎えたWindows7を使い続けるようなものなのだろうか」「新しいのを買わなきゃだめだ。もうだめだ!」などと、いろいろなことを考えてしまうね。
しかしそこであきらめてはいけないのでした。
ただし、それはおじさんが、その古いiPadとは別に、比較的新しいiPadをおじさんが持っていて、この新しいiPadについてはすでに最新のYouTubeをすでにダウンロードしていたからだよ。そのような場合は、古いiPadに古いYouTubeをインストールすることができるんだ。
その方法とは、古いiPadに、
同じiOSアカウントでもって、アプリストアに行って、「購入済み」に進んで、そこでYouTubeを選べば、「おっと、このiPadは古いね。古いやつ用の古いYouTubeをダウンロードしますか」と、なんとまあ親切に聞いてきてくれて、全く問題なく、古いiPadに古いYouTubeがインストールできたんだ。
なんでこんなふうになってるのかはわからない。古いデバイスを使っていた人に、古いデバイスを使い続けることを断念させようとする、Appleの施策の一つなのかどうかさえわからない。まあ、抜け道は残してくれているが、それがわかりにくくなっているというなんとも微妙な演出に思えてしまうね。
今日の内容は以上だよ。チャオ!
ImageShrinkChoppedGaussian 画像縮小アルゴリズム
やあみんな、元気にしてたかな。おじさんはまたも風邪をひいてしまい、先月の風邪で匂いや香りが全くわからなくなったままだというのに泣きっ面に蜂のようなことになってるよ。18℃以下の部屋は脳や心を破壊するという記事も見たので、みんなも部屋は暖かくして過ごすんだぞ。
さて今日はここ数カ月にわたって検討してきた、画像縮小アルゴリズムの、ひとまずな決定版が完成したのでここにメモっておくぞ。カーネル関数は、Lanczos3とかが良いらしいのだけど、よくわからないのでおじさんは今回、ChoppedGaussianというもの勝手にこしらえて採用してみた。先の記事で紹介した平均画素法よりもにじみや欠損が少なく、実装もとてもシンプルになっているのがわかるかと思う。
void image_shrink_chopped_gaussian(LPDWORD src, const int w0, const int h0, const float scale, LPDWORD dst) { int w = w0 * scale; int h = h0 * scale; for (int i = 0; i < h; ++i) { for (int j = 0; j < w; ++j) { DWORD& out = dst[i * w + j]; Pix pix_out(0); float xs = j / scale; float xe = (j + 1) / scale; float ys = i / scale; float ye = (i + 1) / scale; // Lanczos3 というかガウシアン。 { float cx = (xs + xe)*.5; float cy = (ys + ye)*.5; float cx0 = floor(cx); float cy0 = floor(cy); float sum_w = 0; const int n_window = 3; for (int ii = -n_window; ii < n_window + 1; ++ii) { for (int jj = -n_window; jj < n_window + 1; ++jj) { int tx = cx0 + jj; int ty = cy0 + ii; float tx2 = tx + .5; float ty2 = ty + .5; if (tx >= 0 && ty >= 0 && tx < w0 && ty < h0) { float rr = (cx - tx2)*(cx - tx2) + (cy - ty2)*(cy - ty2); float ratio = (rr>4) ? 0 : exp(-rr);// これがChoppedGaussian(r); pix_out += Pix(src[ty*w0 + tx])*ratio; sum_w += ratio; } }// jj }// ii pix_out /= sum_w; } // out = pix_out.GetValue(); }// j }//i return; }
Pixクラスについては一つ前の日記を見てくれ。
やっぱり平均画素法はねえ、一見きれいに縮小できたかにみえるんだけど、よくみると色の「にじみ」がひどかったりだね、なにか欠損みたいなものが出ちゃうんだよね。処理も重いので、平均画素法は、どうやらそういう考え方もありますってだけのもので、それを実装したりする意味はずばりゼロなのだね。はい、「平均画素法は重いし、品質もいまいちなのでだめ」これが一つの結論ですね。
じゃ、ま、最後におじさんがどのレベルの違いについて語っているのかを明らかにする画像サンプルを紹介しよう。左と中央とが今回のChoppedGaussianで、右が平均画素法だよ。これは、上記二つのアルゴリズムを使って、もと画像を70%に縮小させた結果を、それぞれ並べて見やすいように引き延ばした結果なわけだけど、全然違いがわからないという人もいれば、全然違うねという人もいるかと思う。
左と中央の違いについて、中央は上記ソースそのままの結果、左はカーネル関数を、rr>2.56でChopOffし、exp(-rr*3)としたもの、つまり、ぼやけるのを嫌がってカーネル関数をより細くしてみた結果だ。しかし、結果的にみると中央のやつが一番きれいだね。左はぼやけ感軽減にはなるけれども、やっぱりその分欠損も出てしまっている気がする。まあ左と中央とは、使い分けなのかな。用途別で。
じゃ今日はこんなところだ。チャオ!
DWORD化されたRGBA値を人間が扱いやすいようにするクラス
DWORDの中身が、RGBAだとか、BGRAだとか、そういう内部フォーマットはいざ知らず、DWORDのままで色の値の加減算もろくにできやしないので、そういう問題へのソリューションとして、以下便利クラスを作成したメモ。 class Pix { public: Pix(DWORD col) { _r = col & 0x000000ff; _g = (col >> 8) & 0x000000ff; _b = (col >> 16) & 0x000000ff; _a = (col >> 24) & 0x000000ff; } Pix(int r, int g, int b, int a) { _r = r; _g = g; _b = b; _a = a; } ~Pix(void) {} void operator += (const Pix& pix) { _r += pix._r; _g += pix._g; _b += pix._b; _a += pix._a; } void operator /= (const float val) { _r /= val; _g /= val; _b /= val; _a /= val; } Pix operator * (float val) { return Pix(_r*val, _g*val, _b*val, _a*val); } void clamp_0x00_0xff(void) { auto ata = [](int x) { if (x < 0) return 0; else return x; }; auto atap = [](int x) { if (x > 255) return 255; else return x; }; _r = ata(_r); _g = ata(_g); _b = ata(_b); _r = atap(_r); _g = atap(_g); _b = atap(_b); _a = 255; } DWORD GetValue(void) { return _r | (_g << 8) | (_b << 16) | (_a << 24); } int _r, _g, _b, _a; };
具体的な使い方は、一つ前の記事を参照。
平均画素法を2日で実装してみたよ
やあ子供たち。おじさんは風邪をひいてしまって今朝なんかもとても喉が痛いのだけど元気にしてるかい。今回は有名な画像縮小アルゴリズムの一つ、平均画素法を実装してみたのでここにメモしておくよ。
// ここに、平均画素法を実装してください。 // in (LPDWORD)bm.bmBits w, h, scale // out (LPDWORD)lpPixel // 平均画素法 LPDWORD src = (LPDWORD)bm.bmBits; LPDWORD dst = lpPixel; for (int i = 0; i < h; ++i) { for (int j = 0; j < w; ++j) { DWORD& out = dst[i * w + j]; Pix pix_out(0); float xs = j / scale; float xe = (j + 1) / scale; float ys = i / scale; float ye = (i + 1) / scale; int xsi = ceil(xs); int xei = floor(xe); int ysi = ceil(ys); int yei = floor(ye); float cnt = 0; auto pixAddr = [&src, &w0, &h0, &pix_out, &cnt] (int ii_s, int ii_e, int jj_s, int jj_e, float ratio) { for (int ii = ii_s; ii < ii_e; ++ii) for (int jj = jj_s; jj < jj_e; ++jj) if ((ii > 0) && (ii < h0) && (jj > 0) && (jj < w0)) { pix_out += Pix(src[ii*w0 + jj])*ratio; cnt += ratio; } }; // 内部 pixAddr(ysi, yei, xsi, xei, 1.0f); // 四辺 pixAddr(ysi, yei+1, xsi - 1, xsi, xsi - xs); pixAddr(ysi, yei+1, xei, xei + 1, xe - xei); pixAddr(ysi - 1, ysi, xsi, xei+1, ysi - ys); pixAddr(yei, yei + 1, xsi, xei+1, ye - yei); // 四隅 pixAddr(ysi - 1, ysi, xsi - 1, xsi, (xsi - xs)*(ysi - ys)); pixAddr(ysi - 1, ysi, xei, xei + 1, (ysi - ys)*(xe - xei)); pixAddr(yei, yei + 1, xsi - 1, xsi, (xsi - xs)*(ye - yei)); pixAddr(yei, yei + 1, xei, xei + 1, (ye - yei)*(xe - xei)); // 正規化 pix_out /= cnt; // out = pix_out.GetValue(); }// j }//i
ここで使用しているPixクラスはDWORD化されたRGBA値を人間が扱いやすくするための補助クラスだよ。大したものではないがとても便利だからその実装はまた別途未来日記で紹介するとして、まこんな感じのアルゴリズムだな平均画素法は。ceilとかfloorだとかをやってるところがアルゴリズムの本質部分で、まコアといえばコアかな。ratio変数とか。
ただね、ぼやけるんだよね縮小された画像が。定値関数で畳み込みしているだけの処理なのでま当然といえば当然なんだけどさ。なんとかこれをもうちょっとシャープな仕上がりにしたいところだが、そこでこの一つ前の記事の内容がお役立ちになるはずなのだけれども。まそれもまた未来日記で紹介するよ。うまくいったらな。
チャオ!
パスカルの三角形のn段目を取り出せるプログラム
やあ子供たち。今日はパスカルの三角形のn段目を取り出せるプログラムを作ったよ。
以下の、calc_nth_pascal_row()という関数がそれさ。
#include <iostream> #include <vector> using namespace std; void calc_nth_pascal_row( const int n, vector<int>* lv ) { vector<int>& v = *lv; v.push_back(1); for( int i=0; i<n-1; ++i ) { int tmp=1; int tmp2; for( int j=1; j<(int)v.size(); ++j ) { tmp2 =v[j]; v[j] = tmp + tmp2; tmp= tmp2; }// j v.push_back( 1 ); }// i return; } int main( void ) { int n= 10;// ←ここは任意段数を指定。 // for( int i=1; i<11; ++i) { vector< int > v; calc_nth_pascal_row( i, &v ); for( int ii=0; ii<(int)v.size(); ++ii) printf( "%d, ", v[ii] ); printf("\n"); }// i return 0; }
上記のようなコードを、codepadで入力し、LanguageとしてはC++を選んでSubmitすると、
以下のような結果が得られたよ。
1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, 1, 5, 10, 10, 5, 1, 1, 6, 15, 20, 15, 6, 1, 1, 7, 21, 35, 35, 21, 7, 1, 1, 8, 28, 56, 70, 56, 28, 8, 1, 1, 9, 36, 84, 126, 126, 84, 36, 9, 1,
vector使わずに用途に応じて固定長配列なふうに改造すればもっと高速化できそうだね。
用途に応じていろいろと「高速化の余地」があるコードになっているぞ。
そしてこれは、このページと内容一致してるからあってるんだと思う。
どうだこの絶妙さ。tmpと、さらに、tmp2を導入することに気付けたところがポイントかな。
何に使うんだって?画像処理の畳み込み枠に使える重みづけ関数の自動算出にだよ。決まってるじゃないか。
おじさんが意味もなくこんなコードを書こうと思うとでも思ったのかい?
ではまたな、チャオ!