タグ別アーカイブ: 映像

SAR(PAR) DAR ってなんですか?

タグ: , , , | 投稿日: 投稿者:

久々のいかおです

ffmpeg を使っているとぶち当たります(いや、それ以外でも)

私はいつも、えっと、なんだっけ?ってなるので書いておきたいと思いました

sar(par)が1:1の場合は良いのです。ここが妙ちくりんなことになってくると登場人物が不要に痩せたり太ったりします。

SARがSample Aspect Ratioで、PARがPixel Aspect Ratioです

DARはDisplay Aspect Ratio・・・・・

「そんなこたぁ聞いてねぇんだよ」

と言われそう。ここぢゃないんです

ちなみに、SARとPARは同じことです。

で、いっつも悩ましいのは

SARに変な値が入っているとDARもパルプンテ(実は違います)なんですね。だからスッキリするために

計算式をメモるわけです。

SARはpixelの形を表します。ピクセルの横幅と縦幅の比です

DARは出来上がった画像の縦横比になります。

だから、元々のピクセル解像度にそれぞれSARの比率をかけるとDARになるってことです。つまり

元のpixel解像度が

720×480

で、SARが8:9なら

720 x 8 : 480 x 9 = 4 : 3

で、DARが 4:3 となる訳です

じゃあね、

720 x 540 の画像を 16:9 で出したい場合SARはどうなるんですか?

こうするんですよ(私もそのまま覚えこむことにしました)

元画像の高さ x 出したい横幅 : 元画像の幅 x 出したい縦幅比

540 x 16 : 720 x 9 = 8640 : 6480 =  4 : 3 (SARね)

・・・おおおお

720 x 480(ntsc)を16:9で

480 x 16 : 720 x 9 = 32 : 27

おお・・・なんだか呪文っぽくなってきたぞ

そうそう、パルプンテだったやつは

pixelが720×480でSARが 40:33 でDARが 20:11ですってよ!

そうか、そうかDARにするためのSARだから

480 x 20 : 720 x 11 = 40:33

ああ、そういうことだったのか。

私はこの理解に苦しんだんだけど、同じつまづきした人っていないの?


音声の倍速再生

タグ: , | 投稿日: 投稿者:

毎度、いかおです

前回の続きで倍速再生について、特に音声の切り詰めかたを考えてみた。

・音声をある一定の長さできる

・それをつなげる

・のりしろを作る

まずは、これを図にしてみました

baisoku

1番上が元の波形(だと思って!)で、矢印がついてる部分がサンプル対象です。

2番めはサンプル対象をくっつけた図。オレンジの線はその間のサンプル

3番めはのりしろをフェードアウト/フェードインしたところです。ここで赤い線の様になってショックノイズを低減させます

フェードイン/アウト コード例】

#define NORISHIRO 100 // 例えばのりしろは100個
.
.
.
// 音声デコードがされたら
avcodec_decode_audio4(audioCodecCtx, decodedAudio, &decodedSize, &packet);
if (decodedSize > 0){
    size_t packetSize;
    // flgならsample出す
    if(flg){
        // m_sampleCount のりしろ長さ
        packetSize = decodedAudio->nb_samples * sizeof(short) * CHANNEL;
        short *samp = (short *)decodedAudio->data[0];
        flg = false;
        // サンプルの中に左右交互にデータがある(プラナーでない)
        for(int i=0;i < m_sampleCount;i++){
            samp[(i * 2)] = (short)(((float)m_bef[(i * 2)] * (((float)m_sampleCount - (float)i - 1.0f) / (float)m_sampleCount)) +
            ((float)samp[(i * 2)] * (((float)i + 1 ) / (float)m_sampleCount)));
            samp[(i * 2) + 1] = (short)(((float)m_bef[(i * 2) + 1] * (((float)m_sampleCount - (float)i - 1.0f) / (float)m_sampleCount)) +
           ((float)samp[(i * 2) + 1] * (((float)i + 1 ) / (float)m_sampleCount)));
        }
        // ここが再生データ
        decodedAudio->data[0];
        // これが再生データの長さ
        packetSize;
    } else {
        // ! flgならsample保存(フェードアウトに利用)
        if(decodedAudio->nb_samples > NORISHIRO){
            m_sampleCount = NORISHIRO;
        } else {
            m_sampleCount = decodedAudio->nb_samples;
        }
        memcpy((char *)m_bef, (char *)decodedAudio-&gt;data[0], m_sampleCount * sizeof(short) * 2);
        flg = true;
    }
}
.
.
.

ひとまずはこれでさほどノイズが気にならなくなりました。

1度に採れるサンプル数にもよりますが、のりしろは長くとった方がキレイに再生できます


動画の倍速再生の考え方

タグ: , | 投稿日: 投稿者:

こんにちは毎度お騒がせします、いかおです

早送りと遅送り(?) まず、映像だけ考えていきますと・・・・・

1.再生時にフレームレートを偽る

→毎秒30フレームの動画であれば、これを毎秒60フレームの動画として再生すれば2倍速、毎秒15フレームで再生すれば0.5倍速になる。

2.フレームを間引いたり、水増しする

→再生対象フレームを半分にすると2倍速、同じフレームを2回づつ再生すると0.5倍速になる。

1の方法は昔のビデオテープの早送りに似た方法ですね。 デバイスその他が対応すれば、サンプリングレートを自在に操って様々な再生に対応できます。 遅くする方は簡単なのですが、早くする方は場合によって、デバイスが対応しないことがありそうです。

多分、普通は2の方法を採用しますね。ただ、これも2倍、3倍、1/2倍、1/3倍位はたやすいのですが、 1.5倍とか2/3倍とかの割り振りというか、計算は大変そうです。 この様に映像の方は案外簡単に速度変更できます。ま、こういうところは所詮ぱらぱら漫画の延長なので、考え方的にも 楽ですね。

でも、音を考えると意外に難しいのがわかります。

ここで、色んな難しいパターンを排除して、音声の2倍速再生を想像して下さい

【音の2倍速再生をどうするか?】

皆さんならどうします?

映像と同じで音声サンプルを半分間引くと、半分の長さの同じ様な波形ができます。

これは実際、間引いたためのギャップでノイズが多発するのと、そうです、

周波数があがるのです。つまり、音が高くなります。

昔のアナログビデオやカセットテープ(みんな知らないか?)の早送りみたいです。

映像でいくところのフレームレートの偽りと似ています。そんなことなら

最初からサンプリングレートを偽って再生すりゃいいんですよね。

でも、現実的じゃない。じゃ、間引きでいきますか?

「いや、それでいいよね。普通」

っていうなよ絶対。ダメ絶対

音程を上げなくても倍速再生できるんです。

音は実はいろいろ奥が深くて様々な方法があるのですが、概ね

こんな方法が採られます。

・音をある時間的長さのかたまりに分割して考える

・その大きさで間引いて、それぞれをつなぎ合わせる

・つなぐ部分にのりしろを設けて、のりしろ部分で前のかたまりのフェードアウトと次のかたまりのフェードインを合成する

#これはノイズ除去のために行います

実際のコードは次回必ず載せます。

それでは


Video For Linux(v4l)について

タグ: , | 投稿日: 投稿者:

Webカメラなどから撮影した動画を取り込んで××するんならWindowsなら今時はMedia Foundationでしょうか?いまだにDirectShowが便利なんでしょうか?
以前にDirectShowでフィルターを書いて色々実装しましたが、フィルターグラフの接続とかが面倒だったりしました。最近の私の場合はLinux(Ubuntu)なので、DirectShowのお世話になっていません。

以前Linuxでカメラでキャプチャした画像を××するのに実際に私が行ったのは、BlackMagicDesign社のビデオボードDecklink/Intensityシリーズにカメラを接続する形で行いました。BlackMagic Decklink/IntensityにはBlackmagic DeckLink SDKを利用してアクセスし、画像の生データを得ました。

BlackMagic Decklink/Intensityにカメラをさしても/dev/videoXが生えてくるわけじゃないので使えませんでしたが、今度はV4lです

※実際にはOpenCvでキャプチャ処理とかも書きましたが、これはOpenCv内部からv4lを呼んでるんですね

1.オープン

 int open(const char *pathname, int flags);
 で行う。pathnameに"/dev/video0"とか、flagsにはO_RDONLY, O_RDWR
 とか設定

2.カメラ操作
※カメラで操作可能な設定をいじったり、バッファを関連付けたり、画素データを取り込んだりします

int ioctl(int fd, int request, ...);
で行う。リクエスト(request)については下に例示

リクエストの例

VIDIOC_QUERYCAP カメラデバイスに保有する機能を問い合わせます
VIDIOC_S_FMT カメラデバイスに画像データのフォーマットを設定
VIDIOC_G_FMT カメラデバイスに画像データのフォーマットを取得
VIDIOC_REQBUFS 画像データ用のバッファをカメラデバイスに要求
VIDIOC_QUERYBUF VIDIOC_REQBUFSで要求したバッファの情報を取得
VIDIOC_QBUF バッファをエンキューします
VIDIOC_DQBUF バッファをデキューします
VIDIOC_STREAMON キャプチャ開始
VIDIOC_STREAMOFF キャプチャ停止

3.select()で待つ(ってことはpoll()も使えんのか)ことで、キューにデータ入ったか確認する
4.(カメラの)バッファをユーザープログラムにマッピングする

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
で行う

※ユーザー定義のポインタにバッファアドレスをマッピングする

★で、手順的には

  1. カメラデバイスオープン
  2. 設定(解像度など)
  3. バッファ要求
  4. バッファにポインタ割り当て
  5. バッファをキュー(あらかじめ)
  6. キャプチャ開始
  7. デキュー/エンキュー繰り返し→フレームデータの処理
  8. キャプチャ終了
  9. 後始末

となります。

以上ですが、今度は、ちゃんと何かつくりますね。これとffmpegのlibで何か作るとか、OpenCvと組み合わせるとかで・・・・


ffmpegの音声フォーマットがFLTPですって!?

タグ: | 投稿日: 投稿者:

私が自分のアプリケーションに利用していたffmpegのライブラリなんですが、そろそろ最新のffmpegに入れ替えようとしました。
すると、今まで符号付き16ビットとして利用出来ていた動画の音声パケットフォーマット(s16xx)が、FLTP(?)とかになっていました

このタイプをチェックしていたためアプリはパケットを処理せず終了していました。

そこで、

「そんなチェックハズしゃええやん」

とチェックを単に外すと音パケットが全然足りなくなりました。google先生に尋ねるとフォーラムとかで私と同じ境遇の人々がいて質問していました。
その回答で英語で

You need to remember that AV_SAMPLE_FMT_FLTP is a planar mode. If your code is expecting an AV_SAMPLE_FMT_S16 (interleaved mode) output, you need to reorder the samples after converting. Considering 2 audio channels and using interleaved mode, the samples are ordered as “c0, c1, c0, c1, c0, c1, …”. Planar mode is “c0, c0, c0, …, c1, c1, c1, …”.

プラナーって映像とプラナーしてるわけ?
だとしたらどうしたら良い?
と思っていたら。どうやらこれは(例えばステレオなら)
ch0(R)0,1,2,3,4,5,6,7,8…….
ch1(L)0,1,2,3,4,5,6,7,8…….
と音声チェネルがプラナーしてるってことが分かった。そしてさらに、
音声の値もフローティングポイント、すなわちfloat型で値の範囲が-1.0~1.0であることが分かった

フロートの値は符号付き16ビットに変換するのでそれぞれ32768を乗算してやれば、
-32768~32768
になる。そして、
右の1個めを最初に、左の1個めを次に、右の2個めをその次、左の2個めをその次・・・・・・・
とやって元の16bitのフォーマットが取り返せる

で、そのプラナーな要素は以下に格納されている
デコード結果(AVFrame)のextended_data[n] //チャネルnの音声パケット
ということで以下にコード例(ここではステレオ限定で)

//デコードが終わってから
avcodec_decode_audio4(audioCodecCtx, decodedAudio, &decodedSize, &packet);
if(audioCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLTP){
        short *audioPkt   = NULL;					// パケット格納域
        size_t packetSize = 0;						// パケットバイト長
        int    sampleCnt  = audioCodecCtx->decodedAudio->nb_samples;	// サンプル数
        float *channel0   = (float*)decodedAudio->extended_data[0]; // 元の型はuint8_tな
        float *channel1   = (float*)decodedAudio->extended_data[1];
        packetSize        = sampleCnt * 2 * sizeof(short);    // バイトサイズ(ステレオ2ch*sizeof(short)*サンプル数)
        if(packetSize){
            audioPkt = (short *)av_mallocz(packetSize);       // ffmpegなんで・・・
            for(int i=0;i < sampleCnt;i++){       // サンプル数分
                audioPkt[i*2]   = (short)(channel0[i] * 32767.0f);
                audioPkt[i*2+1] = (short)(channel1[i] * 32767.0f);
            }
        }
        // この'audioPkt'が今までの16ビットデータなので。これを使う
} else {
..................
}

※エラーチェックとか、領域の開放とかが抜けてますからね。このままコピペはダメですよ

これ、最初
audioPkt[i*2] = (short)(channel0[i] * 32767.0f);
のところを
audioPkt[i*2] = (short)channel0[i] * 32767.0f;
としてたら全然音が出なかった。
良い子の皆さんなら何故だかわかるよね(私は馬鹿でした)

なぜffmpegがこの様に変更したかとかは知りませんが、以下参考までに
ffmpegを新しくするのに私が遭遇したのは、その他では
・フレームのアロケーションで
avcodec_alloc_frame()を使わないでav_frame_alloc()を使う
・フレームの初期値設定
avcodec_get_frame_defaults()を使わないでav_frame_unref()を使う
・AVCODEC_MAX_AUDIO_FRAME_SIZEの定義が無くなった
→1秒分を想定した長さを入れとけってことで別名で(ここでは)192000を定義しました
以上で動くには動きました。


プレモルの宣伝でやってるエフェクトを真似してみた(その方式).vol2

タグ: | 投稿日: 投稿者:

いかおです。

前回の「プレモルの宣伝でやってるエフェクトを真似して」の続きです。

一つ一つのピクセルがuyvy422でくるとなると、hueへの変換は以下の様になります

まず、RGBにします。
R = 1.000Y + 1.402V
G = 1.000Y – 0.344U – 0.714V
B = 1.000Y + 1.772U

hue = 180 / π * atan2(√3 * (GB), 2 * R – (float)GB)

※double atan2(double y, double x); // math.h のやつね

これでHueがマイナスである場合がありますので、マイナスだったら
360を足します

これでやうやくHueになります。

こんな計算を例えば(たかだか)1080iでやろうとすると
毎秒この計算を、1920x1080x30 回やりますとかなりゴーヂャスなPCでも間に合いません

確かにこのままやったら再生が間に合わなかった
で、最初に全パターン計算しといてテーブルに格納するんです
マッチングテーブルですねぇ・・・

ただ、yuv値ってそれぞれ(この場合)8bitですが、実際の値の範囲は

y 16~235
u(cb)/ v(cr) 16~240

こんな感じになりますが、とりあえず全部作っちゃえってんで、ソースコード公開!!
★RGB全パターンからHueを求めてYUVのテーブルに押し込みます。

.
#define KOSUU     256
.
.
    memset((char *)YUVTable, 0x00, sizeof(uint16_t) * KOSUU * KOSUU * KOSUU);
    double root3 = sqrt(3);
    float  hue;
    for(int ir=0;ir < KOSUU;ir++){
        for(int ig=0;ig < KOSUU;ig++){
            for(int ib=0;ib < KOSUU;ib++){
                float y, u, v;
                uint8_t ucy, ucu, ucv;
                y = (0.299f  * ir) + (0.577f * ig) + (0.114f * ib);
                u = (-0.169f * ir) - (0.331f * ig) + (0.5f   * ib);
                v = (0.5f    * ir) - (0.419f * ig) - (0.081f * ib);
                if((round(y) < 0.0f) || (round(y) > 255.0f)){
                    syslog(LOG_ERR, "Y overfloow at(%d:%d:%d)", ir, ig, ib);
                    continue;
                }
                if((ir == 0) && (ig == 0) && (ib == 255)){
                    u = 255.0f;
                } else if((round(u + 128.0f) < 0.0f) || (round(u + 128.0f) > 255.0f)){
                    syslog(LOG_ERR, "U overfloow at(%d:%d:%d)", ir, ig, ib);
                    continue;
                }
                if((ir == 255) && (ig == 0) && (ib == 0)){
                    v = 255.0f;
                } else if((round(v + 128.0f) < 0.0f) || (round(v + 128.0f) > 255.0f)){
                    syslog(LOG_ERR, "V overfloow at(%d:%d:%d)", ir, ig, ib);
                    continue;
                }
                ucy = (uint8_t)round(y);
                ucu = (uint8_t)round(u + 128.0f);
                ucv = (uint8_t)round(v + 128.0f);
                hue = 180 / M_PI * atan2(root3 * ((float)ig - (float)ib), 2 * (float)ir - (float)ig - (float)ib);
                if(hue < 0.0f) hue = hue + 360;
                YUVTable[(ucy * KOSUU * KOSUU) + (ucu * KOSUU) + ucv] = (uint16_t)round(hue);
             }
         }
     }

これを、Hue中央値とレンジを指定させといて、比較のため”0″変位に置き換えて

 // m_hue   ..中央値
 // m_range ..レンジ
 // m_gap   ..変位値(0変位比較のための加算する値) 
 // こんな風にしといて         
 if((m_hue + m_range) > 360)
    m_high = (m_hue + m_range) - 360;
  else
    m_high = m_hue + m_range;

  if(((int)m_hue - (int)m_range) < 0)
      m_low = ((int)m_hue - (int)m_range) + 360;
  else
      m_low = m_hue - m_range;         if(m_low > m_high){

  m_gap = 360 - m_low;
  m_low = 0;
  m_high = m_high + m_gap;
......
  uint16_t nowHue = YUVTable[(y * KOSU * KOSU) + (u * KOSU) + v] + m_gap;
  if(nowHue > 360) nowHue = nowHue - 360;
  if((nowHue <= m_low) || (nowHue >= m_high)){
      // モノ黒にする
  }

で、前回のサンプルでもいまいちキレイじゃないので、調査の上、
キレイにして掲載します(いずれね)


プレモルの宣伝でやってるエフェクトを真似してみた(その方式)

タグ: | 投稿日: 投稿者:

いかおです

映像関係のネタです

サントリープレミアムモルツのコマーシャルでやっているビールだけが黄金色に輝くエフェクトなんですが、やってみたいと思いました。

実はNFLの中継でピンクリボン期間に放映されたCF(?)で選手が身につけたピンク色のモノだけ発色しているのを見て実際にやってみようと考えたわけです。

先ず、ソースとしてUYVY422が入ってきて特定の色以外はモノにするので各画素のUとVは両方共”128″に塗り替えます(U,Y,Vそれぞれ8bit。1画素あたりは16bitね)。そういった画素データの書き換えで簡単にモノクロにはできるわけですね。そして、特定の色としてヒットした場合はそのまま加工せずにおいておけば、目的の効果が出きるはずですね。これなら数フレームの遅延でライブにも利用できます。

さて、特定の色の指定なのですが、前提がRGBではなくUYVY(CrYCbY)なので隣り合う2つの画素は2画素で1つの色です(キリ!)。ここで、uyvyからrgbを求めたところで色として判定に困るのでさらに変換して色相(Hue)を基準にして判定することにします。今回はここでおしまい。次回はそのHueの求め方から

※ちょっと赤色でやってみた

★元はこんな感じ