2021年3月12日金曜日

顔ランドマーク検出をやってみた。

 先日このブログで、FaceSwapのプログラムを作りました。

だけど、時間がなくて、顔の輪郭部分の点を手動で設定していました。

このため、任意の写真で顔を入れ替えようとすると、点を設定する作業がとてもめんどくさい。

おじさん、任意の写真の顔をもっと簡単に入れ変えたいのです。


そこで、今回はどんな顔でも入れ替えできるように、プログラムを改良してみたと思います。


どうやって顔の輪郭を抜き出すんだろ。

調べてみると、顔の輪郭部分の検出のことを「顔ランドマーク検出」というらしい。

https://qiita.com/RanWensheng/items/d8768395166d041a753a


shape_predictor_68_face_landmarks.datというdlibの学習済みデータを使うのが一般的なようです。

学習済みデータ100Mバイトもあるじゃないか!

しかもdlibというライブラリを使うのね。

こんなことばっかりやっているから、おじさんのハードディスクがどんどんなくなっていく。


こんなかんじでdlibを使うのね。

https://iatom.hatenablog.com/entry/2020/11/01/152334



こんなかんじでdlibとOpevCVでFaceSwapをすれば、動画をどんどん変換できるのか。

https://github.com/MishaPrigara/FaceSwap



というわけで顔ランドマーク検出対応のFaceSwapを作ってみました。

ソースはこちら

https://drive.google.com/file/d/1_F9lTnfBVbme5pmI9WrSfaayQr5sQut_/view?usp=sharing

その辺に落ちている写真で実験。

入れ替え後の写真

元の写真


できた!

これ確かに任意の写真で顔を入れ替えられるようになると面白いね。


2021年3月8日月曜日

プラズマスピーカー

 おじさん、中学生の時に電子工作でラジオ作ったんだよね。

何気なく最近の中学生に、最近はどんな電子工作をするのか聞いてみたところ、「テスラコイル」と言われました。

おじさん、最近の中学生についていけない・・・・・。

テスラコイルって何満ボルトもあるやつでしょ?

電子工作で作れるの?

調べてみると、テスラコイルのキットがアマゾンで600円くらいで売られているじゃないか!

しかも、テスラコイルから発せられるプラズマで音を鳴らす、プラズマスピーカーなるものも作れるらしい。

もう何人も作った人がいるのね。

https://www.youtube.com/watch?v=sOr_7gOXjgk


というわけで、おじさんも数年遅れでアマゾンに注文して、プラズマスピーカーを作ってみました。

すげー。プラズマで本当に動く。

あんまり音はよくないけれど、この分解能というか解像度感はすごいね。

振動版がなくても音ってならせるんですね。

ということは原理的には雷のゴロゴロ音の代わりにも音楽ならせるはず。


昔はこういう面白いキットは日本でしか売ってなかったんですが、最近はすごい面白いキットが中国でたくさん作られているんですね。

日本が技術大国であったのはもう過去なのね。


2021年3月3日水曜日

FaceSwapをやってみた。

最近ブログを更新していませんでした。

画像処理やAI系で個人で数時間でできそうな面白いネタがあまりないんだよねぇ。


暇なので、相棒のテレビをみていたら、動画の顔を別の人の顔に変えるDeepfakeというソフトがあるらしい。

なんかいろいろ悪いことに使われそう。

FaceSwapという技術で動画から静止画を切り出して一枚一枚変換しているのね。

ソースを見てみたら、おじさんの大嫌いなPythonで書かれているじゃないか!


C++でできないかと思っていたところC++のFaceSwapのソースを見つけました。

https://learnopencv.com/face-swap-using-opencv-c-python/

これならば数時間で改造して遊ぶことができる。

改造して、以前作った最小のOpenCVで動くようにしてみました。



みんな、トランプ大統領の顔を変えて実験するのね。

おーなんかできた。

ソースコードも意外と短い。

----------------------------

#include "opencv2_core.hpp"

#include "opencv2_imgproc.hpp"

#include "opencv2_imgproc_imgproc_c.h"

#include "opencv2_imgcodecs.hpp"

#include "opencv2_photo.hpp"


#include <iostream>

#include <fstream>

#include <string> 

using namespace cv;

using namespace std;


//Read points from text file

vector<Point2f> readPoints(string pointsFileName){

vector<Point2f> points;

ifstream ifs (pointsFileName.c_str());

    float x, y;

int count = 0;

    while(ifs >> x >> y)

    {

        points.push_back(Point2f(x,y));


    }


return points;

}


// Apply affine transform calculated using srcTri and dstTri to src

void applyAffineTransform(Mat &warpImage, Mat &src, vector<Point2f> &srcTri, vector<Point2f> &dstTri)

{

    // Given a pair of triangles, find the affine transform.

    Mat warpMat = getAffineTransform( srcTri, dstTri );

    

    // Apply the Affine Transform just found to the src image

    warpAffine( src, warpImage, warpMat, warpImage.size(), INTER_LINEAR, BORDER_REFLECT_101);

}



// Calculate Delaunay triangles for set of points

// Returns the vector of indices of 3 points for each triangle

static void calculateDelaunayTriangles(Rect rect, vector<Point2f> &points, vector< vector<int> > &delaunayTri){


// Create an instance of Subdiv2D

    Subdiv2D subdiv(rect);


// Insert points into subdiv

    for( vector<Point2f>::iterator it = points.begin(); it != points.end(); it++)

        subdiv.insert(*it);         


vector<Vec6f> triangleList;

subdiv.getTriangleList(triangleList);

vector<Point2f> pt(3);

vector<int> ind(3);


for( size_t i = 0; i < triangleList.size(); i++ )

{

Vec6f t = triangleList[i];

pt[0] = Point2f(t[0], t[1]);

pt[1] = Point2f(t[2], t[3]);

pt[2] = Point2f(t[4], t[5 ]);


if ( rect.contains(pt[0]) && rect.contains(pt[1]) && rect.contains(pt[2])){

for(int j = 0; j < 3; j++)

for(size_t k = 0; k < points.size(); k++)

if(abs(pt[j].x - points[k].x) < 1.0 && abs(pt[j].y - points[k].y) < 1)

ind[j] = k;


delaunayTri.push_back(ind);

}

}

}



// Warps and alpha blends triangular regions from img1 and img2 to img

void warpTriangle(Mat &img1, Mat &img2, vector<Point2f> &t1, vector<Point2f> &t2)

{

    

    Rect r1 = boundingRect(t1);

    Rect r2 = boundingRect(t2);

    

    // Offset points by left top corner of the respective rectangles

    vector<Point2f> t1Rect, t2Rect;

    vector<Point> t2RectInt;

    for(int i = 0; i < 3; i++)

    {


        t1Rect.push_back( Point2f( t1[i].x - r1.x, t1[i].y -  r1.y) );

        t2Rect.push_back( Point2f( t2[i].x - r2.x, t2[i].y - r2.y) );

        t2RectInt.push_back( Point(t2[i].x - r2.x, t2[i].y - r2.y) ); // for fillConvexPoly


    }

    

    // Get mask by filling triangle

    Mat mask = Mat::zeros(r2.height, r2.width, CV_32FC3);

    fillConvexPoly(mask, t2RectInt, Scalar(1.0, 1.0, 1.0), 16, 0);

    

    // Apply warpImage to small rectangular patches

    Mat img1Rect;

    img1(r1).copyTo(img1Rect);

    

    Mat img2Rect = Mat::zeros(r2.height, r2.width, img1Rect.type());

    

    applyAffineTransform(img2Rect, img1Rect, t1Rect, t2Rect);

    

    multiply(img2Rect,mask, img2Rect);

    multiply(img2(r2), Scalar(1.0,1.0,1.0) - mask, img2(r2));

    img2(r2) = img2(r2) + img2Rect;

    

    

}



int main( int argc, char** argv)

{

//Read input images

    string filename1 = "ted_cruz.jpg";

    string filename2 = "donald_trump.jpg";

    

    Mat img1 = imread(filename1);

    Mat img2 = imread(filename2);

    Mat img1Warped = img2.clone();

    //Read points

vector<Point2f> points1, points2;

points1 = readPoints(filename1 + ".txt");

points2 = readPoints(filename2 + ".txt");

    

    //convert Mat to float data type

    img1.convertTo(img1, CV_32F);

    img1Warped.convertTo(img1Warped, CV_32F);

    

    

    // Find convex hull

    vector<Point2f> hull1;

    vector<Point2f> hull2;

    vector<int> hullIndex;

    

    convexHull(points2, hullIndex, false, false);

    

    for(int i = 0; i < hullIndex.size(); i++)

    {

        hull1.push_back(points1[hullIndex[i]]);

        hull2.push_back(points2[hullIndex[i]]);

    }


    

    // Find delaunay triangulation for points on the convex hull

    vector< vector<int> > dt;

Rect rect(0, 0, img1Warped.cols, img1Warped.rows);

calculateDelaunayTriangles(rect, hull2, dt);

// Apply affine transformation to Delaunay triangles

for(size_t i = 0; i < dt.size(); i++)

    {

        vector<Point2f> t1, t2;

        // Get points for img1, img2 corresponding to the triangles

for(size_t j = 0; j < 3; j++)

        {

t1.push_back(hull1[dt[i][j]]);

t2.push_back(hull2[dt[i][j]]);

}

        

        warpTriangle(img1, img1Warped, t1, t2);


}

    

    // Calculate mask

    vector<Point> hull8U;

    for(int i = 0; i < hull2.size(); i++)

    {

        Point pt(hull2[i].x, hull2[i].y);

        hull8U.push_back(pt);

    }


    Mat mask = Mat::zeros(img2.rows, img2.cols, img2.depth());

    fillConvexPoly(mask,&hull8U[0], hull8U.size(), Scalar(255,255,255));


    // Clone seamlessly.

    Rect r = boundingRect(hull2);

    Point center = (r.tl() + r.br()) / 2;

    

    Mat output;

    img1Warped.convertTo(img1Warped, CV_8UC3);

seamlessClone(img1Warped,img2, mask, center, output, NORMAL_CLONE);


imwrite("output.jpg", output);

    

    //imshow("Face Swapped", output);

    //waitKey(0);

    //destroyAllWindows();

    


return 1;

}

----------------------------




2020年10月7日水曜日

電流帰還アンプを作る

おじさん、LM380非革命アンプとLM386革命アンプの基板を注文したのですが、到着までに3週間程度かかるみたいで、後に注文したLM380非革命アンプの基板がまだ届きません。

一回も回路のミスをしなくても、基板をつくって、部品を購入して、はんだ付けをし、動作確認するまで1か月以上かかるようです。

回路図をミスってもう一度作ると二か月かかるのかぁ。


LM386の革命アンプしか作ってないけど、革命アンプシリーズは音がよくてびっくりしました。

あまり、大きい音が出せないマンション暮らしのおじさんには1W程度の出力のLM380の子分のLM386で十分です。

いろいろ調べてみると、なんと、革命アンプより音が良いらしい、電流帰還アンプというものがあるらしい。

それはさっそく作ってみなければ。

おじさん、「LM386とLM380さえあればなんでもできる」と思っているので、本日はLM380とLM386で電流帰還アンプを作ってみたいと思います。

http://c3plamo.blog.fc2.com/blog-entry-1868.html


http://settembre21x.blogspot.com/2014/09/lm380.html


世の中にはすごい人がたくさんいまして、もうとっくにLM380とLM386で回路図をシミュレーションしている人がいるので、そのまま基板を作って注文するだけ簡単に高性能アンプが作れます。


本当に革命アンプより音がいいのかなぁ。


LM380電流帰還アンプ回路図

LM380電流帰還アンプ配線図

LM380電流帰還アンプKicadデータ

LM386電流帰還アンプ回路図
LM386電流帰還アンプ回路図




LM386電流帰還アンプKicadデータ

C基板サイズよりちょっと大きめの、50mm*80mmのブレッドボートサイズの基板できりきり入る大きさ。
この大きさで作ればミント缶とかいろいろな小型ケースに収まります。
高さ的にミント缶には絶対入らないのですが、おじさん意地でもこのブレッドボートサイズに詰め込みます。

結果がわかるのは一か月後。

2020年9月23日水曜日

レールスプリッター

おじさん、最近ヘッドフォンアンプを作ろうとしてレールスプリッターではまってしまいました。


オペアンプのICを使おうとするときに最初につまづく負の電圧源。

よくオペアンプの教科書に+5Vと-5Vを作らないといけないなんて書かれています。

おじさんも大学生の時にWebなんてなかったから、どうやって負の電圧を作ればいいのか何か月もなやんだしなぁ。

この負の電圧を仮想的に簡単に作るのがレールスプリッター。


https://nabe.adiary.jp/vgnd


レールスプリッターをつかうと、オペアンプのICがACアダプターなど単一電源で使えます。こんなに便利な回路なのですが、なぜか「定本 opアンプ回路の設計」の本とかにもレールスプリッターのことが書かれていません。なんで?


こんな感じで不安定なレールスプリッターでもよいんですが、

ネットに落ちている回路図はこんなかんじでTIのTLE2426のICを使うものが多いです。

しかしここ数か月、秋月電子でなぜかTIのICが売っていません。


【質問】TI社の半導体製品が生産終了でないのに在庫限りです。どうしてですか?/TIのIC単体を購入できませんがどうしてですか?

[2020/07/21 15:13:44]

【回答】メーカーの方針によりエンドユーザーを特定できない流通経路での販売が禁止されたため、今後の入荷の予定はございません。ご迷惑とご心配をおかけして申し訳ございません。何卒ご理解の程お願い申し上げます。なお、互換品がございましたら是非情報をお寄せください。


ChinaPostから届く、電子工作のキットとかレギュレーターICとかオペアンプとかって結構偽物の部品が届くしなぁ。ダイソーとかで売っている電気小物の回路図も必ずどこか間違えてるし。TIも信頼できるところにしか自分たちの部品を売らないらしい。

米中貿易戦争の影響がこんなところにも来ているのか。


でもレギュレーターもオペアンプもロジックICもオーディオアンプもほとんどのアナログICってTIじゃないか!

なんで正規品が売っている店で正規品が売れなくなるんだよ!

どうやってTIのICを使わずにオペアンプの電源を作ればいいんだよ!

そもそもオペアンプの電源を作るのにオペアンプがいるってなんで??

などいろいろな疑問がたくさんわいてきます。


世の中にはすごい方がいらっしゃいまして、もうとっくにTIのICを使わない、高性能のレールスプリッターを作っている方がいらっしゃいました。しかも、安いし高出力だし、かなりすごい。


http://settembre21x.blogspot.com/2016/05/njm386lm3866v.html


おじさんも大学時代にLM38xがあれば、負の電圧も作れるし、増幅もできるからオペアンプいらないじゃんと思っていたんですよね。

LM386だとTIだけじゃなく新日本無線とか台湾メーカーからも出てるICなので、もうTIのICなんか使わない。



2020年9月10日木曜日

LM380非革命アンプとLM386革命アンプを作る

おじさんみたいなエンジニアは年を取ってくるとアンプを作りたくなるみたいです。

なんでみんなじじいになるとアンプをつくるんだろう?

おじさん、前回のUSB切替機を作ったときに、Kicadの使い方と格安プリント基板の発注方法がわかったので、調子にのってアンプの基板も作ってみました。


以前このブログで紹介した、LM386のICを使った「LM386革命アンプ」とLM380のICを使った「LM380非革命アンプ」の二種類の基板を作ってみました。


LM380非革命アンプ

http://settembre21x.blogspot.com/2013/08/lm380.html


LM386革命アンプ

http://settembre21x.blogspot.com/2013/09/lm386.html


このサイトの情報をもとに基板を作ります。

ユニバーサル基板でアンプを作るととてもめんどくさいので、このプリント基板を欲しいと思っている人、結構いると思うんだよね。

おじさん、人類の資産である回路図と基板データを全部デジタル化。

少しでも世の中のためになればと思っています。


オリジナルと同じように、ユニバーサル基板風に配線するのか、プロっぽくべたグランドにして鋭角にならないように配線するのか迷う。

とりあえず、実験しやすいように、コネクターはACアダプター、3.5mmジャック、ターミナルブロック、配線はユニバーサル基板風にしてみました。


LM380非革命アンプ回路図

LM380非革命アンプ基板

LM380非革命アンプKicadデータ


LM386革命アンプ回路図
LM386革命アンプ基板

いつも、もう少しきちんと作り方をまとめたり、基板の配線すればいいかなぁと思っているのですが、おじさん8割くらい完成するとそこでやる気がなくなっちゃうのです。

秋月電子で革命アンプキットが売れるんじゃないかと思っているのですが、やっぱうれないのかなぁ。


2020年9月8日火曜日

USB切り替え機を作る

 家からのテレワークではどうしてもできない、PCのUSBの抜き差し。

おじさん、このUSBの抜き差しさえどうにかできれば、会社に行かなくてもよいのに・・・・。

USB切り替え機ってちゃんと規格を守って作ると大変なのか、なぜか売っていません。

これ前からずっとほしいのに誰も作ってくれない・・・・・。

なので自分でUSB切り替え基板を作ってみました。


https://akizukidenshi.com/catalog/g/gI-11516/

TLP241というフォトカプラはON時の抵抗が超低抵抗の0.1オームなので、これを使うと小信号系のものは大体何でもON/OFF制御できます。

こんな感じでフォトカプラをアナログスイッチの代わりで使えばよいんですね。



おじさん不器用だし老眼なので、秋月電子で売っているDIPサイズの部品しかはんだ付け付けできません。

知恵を振り絞り、秋月電子で売っている部品だけで回路を作ります。

とりあえず回路図と基板はできた。

kicadのデータはこちら

https://drive.google.com/file/d/1vQ7F435evCeMMdOw_tbZDoyq0ELVTZia/view?usp=sharing


FusionPCBに基板を発注します。

小さい基板ならば10枚5ドルで作れるらしい。

めっちゃ安い。

でも基板は5ドルでできるんですが、送料が15ドルって・・・。


Kicadで秋月電子で売っている部品だけで回路図を作ったり、アートワークする方がはるかに時間がかかりコストが高いので、「秋月電子の部品だけで作るKicadデータ集」みたいなものを公開しているサイトとかないのかなぁ。