Gabung jadi satu

Hampir 4 tahun yang lalu saya memotong2 blog saya ini menjadi blog ngoprek dan blog “lain-lain”. Lalu isi dari halaman ini saya bekukan supaya tulisan lama masih tetap ada namun hanya untuk dilihat saja.

Entah kesambet apa bbrp hari yang lalu, saya memutuskan untuk menggabungkan semuanya menjadi satu lagi :D Semua tulisan dari blog pecahan sudah saya masukkan lagi ke blog yang ini. Semua URL yang menuju blog pecahan (mudah2an) juga sudah saya atur sedemikian rupa supaya bisa menunjuk ke blog ini. Mengapa? karena Cool URIs don’t change :P Euh, jadi inget dulu pas melakukan migrasi pertama dari bBlog (sudah tewas ternyata) ke Drupal xD

Selamat menikmati :P

Tagged | 1 Comment

Improvisasi

Salah satu fitur yang ada di OS X adalah penyuaraan tulisan alias “Text to Speech”. OS X menyediakan sebuah aplikasi command line yang memungkinkan kita untuk meyuarakan tulisan apa saja yang kita masukkan

$ say "How are you?"

Dari sekian penyuara yang tersedia, salah satunya adalah Damayanti yang bisa menyuarakan Bahasa Indonesia

$ say -v Damayanti "Apa kabar?"

Ternyata si Damayanti ini bisa berimprovisasi menambahkan kata yang tidak kita tuliskan di sana. Coba saja jalankan perintah berikut.

$ say -v Damayanti "Sudah 5 hari aku berdiri selama 5 menit dan 5 detik setiap 5 jam"

Tagged , | 2 Comments

Eksperimen

Mari kita coba gabung blog teranggurkan ini dengan Google Plus :D

2 Comments

Memulai OpenGL ES dengan SDL

Saya ingin membuat tulisan berseri mengenai OpenGL dan inilah tulisan pertamanya :D mudah2an bener2 lanjut terus xD

Untuk masuk ke “dunia” OpenGL, hal pertama yang harus dibuat adalah sebuah tempat dimana kode OpenGL kita dapat bekerja. Salah satu caranya adalah dengan menggunakan SDL yang sudah sangat membantu untuk urusan membuat window, menangkap event (mouse, keyboard), dan juga membuat dunia OpenGL kosong yang siap diisi.

Berhubung sekarang dunia mobile sedang sangat meriah, saya juga akan menggunakan standar OpenGL ES yang untungnya juga dapat dijalankan di atas komputer desktop. Untuk bahasa pemrograman, saya akan menggunakan C++ dengan harapan kode akan dapat dengan mudah dipindahkan ke platform lain seperti Android maupun iOS.

Pastikan pustaka pengembangan SDL dan OpenGL ES sudah terpasang. Bagi pengguna keluarga Debian, pasang paket libsdl1.2-dev dan libgles2-mesa-dev.

$ sudo apt-get install libsdl1.2-dev libgles2-mesa-dev

Mari kita mulai..

Pertama-tama, inilah kode pembuka kita :D

#include &ltSDL/SDL.h>

int main(int argc, char** argv) {
  const int width = 1024;
  const int height = 768;
  const int depth = 32;

  // Prepare SDL
  SDL_Init(SDL_INIT_EVERYTHING);
  SDL_SetVideoMode(width, height, depth,
                   SDL_HWSURFACE | SDL_GL_DOUBLEBUFFER | SDL_OPENGL);

  SDL_Event event;

  bool running = true;
  while (running) {
    // Check for incoming event
    while (SDL_PollEvent(&event)) {
      if (event.type == SDL_KEYUP &&
          event.key.keysym.sym == SDLK_ESCAPE) {
        running = false;
      }
    }

    // Swap buffer
    SDL_GL_SwapBuffers();
  }

  SDL_Quit();

  return 0;
}

Simpan ke dalam berkas, katakanlah dengan nama 01-begin.cc, lalu kompilasi dengan perintah berikut:

$ g++ -o 01-begin 01-begin.cc `sdl-config --libs --cflags`

Jika aplikasi ini dijalankan, akan muncul sebuah window dengan isi “acak”. Kita belum memerintahkan penggambaran apa2 sehingga isi window dapat berisi apa saja :D . Berikut ini yang saya dapatkan sekarang.

$ ./01-begin

Untuk mengakhiri, tekan tombol escape.

Kode di atas dapat didapat juga di https://github.com/fajran/opengles-tutorial/tree/master/01-begin

Setelah kerangka dasar siap, mari kita mulai menyentuh OpenGL ES. Agar fungsi-fungsi OpenGL ES dapat kita gunakan, maka pastikan kita sudah mengimpor header dari OpenGL ES

#include <GLES2/gl2.h&gt

Setelah itu kita siapkan 3 buah fungsi “utama” yang digunakan untuk menyiapkan OpenGL ES, untuk memperbarui “kondisi” OpenGL ES, dan terakhir untuk menggambar ke layar.

void init_opengl() {
  glClearColor(0xCD / 255.0, 0xD7 / 255.0, 0xB6 / 255.0, 1.0);
}

void update_opengl() {

}

void draw_opengl() {
  glClear(GL_COLOR_BUFFER_BIT);
}

Lalu kita selipkan pemanggilan 3 fungsi di atas dalam kode yang pertama kita buat. Fungsi pertama dipanggil setelah SDL siap dipakai. Lalu fungsi kedua dan ketiga dipanggil di dalam “main loop” yang akan menggambar setiap frame yang akan kita buat. Kode akhir dapat dilihat di GitHub.

Mari kita bahas sedikit apa yang baru saja ditambahkan.

Pertama, pemanggilan fungsi glClearColor digunakan untuk mengatur warna yang akan digunakan saat layar kita “hapus”. Pada contoh di atas, saya menggunakan warna hijau muda. Berhubung saya cuma tahu kode heksadesimalnya (rentang 0-255) dan fungsi tersebut meminta kode warna dalam tipe float dengan rentang 0.0 sampai 1.0, maka saya konversi dulu nilai warnanya.

Lalu untuk setiap frame yang digambar, saya perintahkan OpenGL untuk menghapus layar dengan memanggil fungsi glClear dengan memberikan paramter GL_COLOR_BUFFER_BIT agar buffer warna yang menampung gambar dihapus dan diisi dengan warna yang saya set sebelumnya.

Mari kita lihat hasilnya sekarang!

$ g++ -o 02-background 02-background.cc `sdl-config --libs --cflags` -lGLESv2
$ ./02-background

Sekian dulu tulisan pembuka ini. Untuk selanjutnya kita akan mencoba sesuatu yang bernama shader!

Semua kode yang akan saya buat dalam seri tulisan ini tersedia juga di https://github.com/fajran/opengles-tutorial dan silakan dipakai sebebas-bebasnya karena semua kode yang terlibat saya rilis dalam domain publik alias public domain.

Tagged , , , | Leave a comment

Transparent video?

For the past few days, I have been wondering how to show a transparent video on an OpenGL scene. I can already extract video frames from a movie file using ffmpeg and display them as a video. But how about a transparent video?

Is there a compression format that supports alpha channel? How is the compression ratio, is it still good? If I simply use chroma key, how can I candle a fade-in/out images (I don’t want to deal with a complicated math formula here)?

All of my questions here were answered after reading this StackOverflow question. Well not answered per se :P , but I immediately what I need to do to show a transparent video. Basically I need to store the alpha mask. But instead of using the alpha channel in a RGBA video stream (I still need to make experiments with this though), I can use an additional area of the video to store the mask. With this, I can use the regular RGB-channel video with those already-known compression format, ratio, and what-not. But in exchange, I need to enlarge the video dimension to store the alpha mask. Hopefully the compression algorithm is clever enough to compress more this part of the video :P

Having that, all I need to do next is to modify the already-simple fragment shader that I use. So now I need to take the first half of video and use the RGB values of it. Then take the second half and get the Red value (or Blue or Green) and use it as the alpha channel of the RGB.

varying vec2 texcoord;
uniform sampler2D textureId;

void main() {
    vec2 tc_rgb = texcoord * vec2(1.0, 0.5);
    vec2 tc_alpha = tc_rgb + vec2(0.0, 0.5);
    vec4 frame = texture2D(textureId, tc_rgb)
    vec4 alpha = texture2D(textureId, tc_alpha);
    gl_FragColor = vec4(frame.rgb, alpha.r);
}

Yippy, that’s all! Ah yes, don’t forget to enable GL_BLEND and set the blend function otherwise you will have a bad time :P

Tagged , , | 3 Comments

Video transparan dengan shader

Main-main dengan OpenGL shader memang mengasikkan :D Katakanlah saya punya sebuah video yang ingin saya tampilkan dalam OpenGL scene. Saya bisa memakai ffmpeg tuk mengekstrak setiap frame yang ada di dalamnya. Frame ini lalu diubah menjadi OpenGL texture sehingga dapat ditampilkan dalam OpenGL scene. GPU dan drivernya zaman sekarang sudah mendukung NPOT Texture alias tekstur yang ukurannya tidak perlu angka dari dua pangkat sekian sehingga setiap frame dari video bisa langsung begitu saja digunakan tanpa harus diubah ukurannya terlebih dahulu.

Kalau cuma menampilkan yang seperti ini, vertex dan fragment shader berikut sudah cukup tuk digunakan. Katakanlah mpv berisi model-view-projection matrix, coord adalah koordinat kotak video (range 0.0-1.0), textureId adalah (tentunya) texture id dari video frame.

attribute vec3 coord;
uniform mat4 mpv;
varying vec2 texcoord;

void main() {
   gl_Position = mpv * vec4(coord, 1.0);
   texcoord = coord;
}
varying vec2 texcoord;
uniform sampler2D textureId;

void main() {
    gl_FragColor = texture2D(textureId, texcoord);
}

Beberapa hari belakangan saya mencari2 cara tuk menampilkan video yang transparan. Satu teknik yang terpikirkan langsung oleh saya adalah dengan menggunakan Chroma Key sehingga saya cukup mencari warna tertentu dan menghapusnya agar tidak ditampilkan. Namun kalau pakai cara ini, bagaimana menampilkan gambar yang tampil/hilang secara bertahap (err.. fade-in/out)?

Setelah merenung lebih lanjut, saya jadi terpikir kalau saya punya akses ke alpha channel saya bisa menggunakan nilai si alpha untuk membuat warna RGB menjadi transparan. Namun apa artinya si video harus menampung gambar dengan channel RGBA? apa format kompresi yang banyak dipakai (spesifiknya h264) itu mendukung RGBA? Untung saja saya menemukan sebuah pertanyaan di StackOverflow yang menjawab pertanyaan saya ini :D

Intinya, video diubah menjadi seperti berikut ini.

Bagian atas video berisi channel RGB dan bagian bawah video adalah alpha masknya. Kalau sudah begini, si video sendiri bisa dikompresi dengan format apapun karena pada dasarnya si video hanya berisi RGB stream biasa. Namun kita perlu melakukan sedikit usaha tambahan pada saat menampilkannya: ambil setengah gambar atas dan setengah gambar bawah, ambil nilai RGB dari gambar atas, ambil nilai (katakanlah) R dari gambar bawah, gabungkan kedua nilai ini dg menjadikan nilai R dari gambar bawah sebagai nilai Alpha dari warna akhir.

Kalau kalimat terakhir di atas diubah menjadi fragment shader, hasilnya kira2 akan seperti berikut.

varying vec2 texcoord;
uniform sampler2D textureId;

void main() {
    vec2 tc_rgb = texcoord * vec2(1.0, 0.5);
    vec2 tc_alpha = tc_rgb + vec2(0.0, 0.5);
    vec4 frame = texture2D(textureId, tc_rgb)
    vec4 alpha = texture2D(textureId, tc_alpha);
    gl_FragColor = vec4(frame.rgb, alpha.r);
}

Voila! eiya, jangan lupa nyalakan GL_BLEND dan set blend function-nya agar alpha value-nya beneran terpakai.

Tagged , , , | Leave a comment

Bongkar pasang berkas video

Contekan tuk bongkar pasang berkas video pakai ffmpeg

Ekstrak audio stream dan ubah formatnya menjadi AAC

$ ffmpeg -i input.avi -vn -ac 2 -ar 44100 -ab 196k -acodec libvo_aacenc audio.aac

Ekstrak frame dari video stream menjadi PNG sequence:

$ ffmpeg -i input.avi -r 30 -f image2 frame-%010d.png

Gabung sederet gambar PNG menjadi berkas video:

$ ffmpeg -f image2 -i frame-%010d.png -vcodec libx264 -b 128k -r 30 video.mp4

Gabung aliran video dan suara menjadi satu:

$ ffmpeg -i audio.aac -i video.mp4 -acodec copy -vcodec copy result.mp4
Tagged | Leave a comment

Di balik Python Trivia #1 dan #2

Daripada blog ini kembali kosong, mari kita bahas Python Trivia babak pertama dan babak kedua yang saya keluarkan lebih dari satu tahun lalu! Mari kita mulai dari babak pertama.

Berikut ini cuplikan kode yang saya tulis:

class Kantong(object):
    def __init__(self, data=[]):
        self.data = data
    def add_data(self, angka):
        self.data.append(angka)
    def cetak(self):
        print 'Daftar angka:', self.data

Lalu mari kita pakai kelas di atas seperti berikut.

satu = Kantong()
satu.add_data(1)
satu.add_data(2)
satu.cetak()

Apa keluarannya?

Daftar angka: [1, 2]

Data yang kita miliki adalah [1, 2] yang berasal dari 2 pemanggilan add_data() dengan parameter 1 dan 2. Gak ada yg protes kan? :D

Setelah itu, kita buah sebuah objek lain dari kelas yg sama. Lalu 3 buah data baru dimasukkan dan lalu dicetak.

dua = Kantong()
dua.add_data(3)
dua.add_data(4)
dua.add_data(5)
dua.cetak()

Hasilnya adalah.. jreng jreng jreng..

Daftar angka: [1, 2, 3, 4, 5]

Lho lho lho.. kok ada [1, 2] dan bukan cuma [3, 4, 5] padahal kita cuma memasukkan 3 angka di objek baru ini?

Mengapa begini mengapa begitu?

Kalau kita baca kelas Kandong sekali lagi, fungsi add_data akan memasukkan angka ke variabel self.data. Variabel ini diinisialisasi pada bagian constructor __init__() dan nilai awal yg dimasukkan adalah nilai yang ada dalam variabel data dari parameter si constructor.

Jika parameter pada constructor ini tidak diisi, maka secara default nilai data adalah sebuah list kosong []

...
def __init__(self, data=[]):
...

Objek satu dan dua yang dibuat dari kelas Kantong ini tidak menyertakan parameter saat objek dibuat. Alhasil, nilai [] tadi akan dijadikan nilai awal untuk self.data.

Pertanyaannya adalah kapan nilai [] dibuat? Ternyata nilai ini dibuat hanya satu kali pada saat kelas didefinisikan! Artinya, jika nilai tadi tidak diganti, semua objek yang berasal dari kelas Kantong akan memiliki variabel self.data yang sama! Walau tersebar di banyak objek, tapi variabel tersebut menunjuk ke sebuah list yang sama sehingga jika ada penambahan/pengurangan isi list tersebut, semua objek yang memegang list tersebut akan dapat melihatnya.

Lalu bagaimana?

Jika yang kita maksud dengan tidak memberikan parameter adalah sebuah list baru (dan kosong) akan dibuat, maka kelas di atas bisa diubah menjadi berikut.

class Kantong(object):
    def __init__(self, data=None):
        if data is None:
            self.data = []
        else:
            self.data = data
    ...

Dengan demikian, jika parameter tidak dimasukkan (yang artinya data akan bernilai None), variabel self.data akan diisi dengan sebuah list baru yang berbeda dengan list dari objek lain dari kelas yang sama.

Kesimpulannya, pada cara pertama, nilai self.data secara default akan diisi sebuah list yang dibuat pada saat kelas didefinisikan. Pemanggilan constructor (juga termasuk fungsi) akan menggunakan list yang sama dan efeknya adalah adanya penggunaan data bersama. Sedangkan pada contoh kelas ke dua, nilai self.data dibuat pada saat constructor/fungsi dijalankan sehingga pada setiap eksekusi, list baru dan berbeda akan dibuat dan efeknya tidak akan ada penggunaan data bersama.

Bagaimana dengan trivia babak kedua?

Kasus sama juga terjadi di sini.

a = [1] * 5

Kode di atas dapat dibaca seperti berikut. Buat sebuah list baru yang isinya adalah isi list sekarang yang diulang sebanyak 5 kali. Nilai dari a adalah seperti berikut.

[1, 1, 1, 1, 1]

Jika kita ubah salah satu elemennya..

a[3] = 200

Maka nilai si a akan menjadi..

[1, 1, 1, 200, 1]

Masih oke kan? :D Apa yang terjadi jika kita menggunakan dict dan bukan angka 1?

b = [{}] * 5

Nilai si b tentunya akan menjadi seperti berikut.

[{}, {}, {}, {}, {}]

Bagimana jika salah dict diubah isinya?

b[0]['a'] = 100

Baris kode di atas artinya ambil elemen nomor 0 dari b (yaitu sebuah dict), lalu kita set kunci 'a' supaya bernilai 100. Bagaimana jika kita cetak nilai si b?

[{'a': 100}, {'a': 100}, {'a': 100}, {'a': 100}, {'a': 100}]

Sesuatu yang (mungkin) tidak diharapkan terjadi lagi :D Lagi-lagi hal ini terjadi karena sebenarnya 5 buah dict yang ada di dalam b sebenarnya adalah dict yang sama namun dimasukkan sebanyak 5 kali di dalam list b.

Mudah2an jelas :D

Tagged | Leave a comment

Version control system

.. is not (just) a backup tool.

Come on, don’t just periodically make “update” commits. Show the progress of what you are doing (development, right?).

Leave a comment

Mainan baru

… namanya Augmented Reality.

Khusus tuk yg satu ini, saya akan menuliskan perjalanan saya di lapak sebelah: arfriends.net. Situs tersebut adalah sebuah forum yang diharapkan akan menjadi tempat bertukar pikiran, berbagi ilmu, berbagi karya, dsb mengenai Augmented Reality di Indonesia.

Tulisan pertama saya: Instalasi FlashDevelop :D

Sebelum mulai bikin ini itu, mari kita persiapkan lingkungan kerja :D Nah FlashDevelop ceritanya menjadi IDE pilihan karena gratis (dan bebas!). Wah kok Flash? xD Sayangnya Flash saat ini masih menjadi satu platform yang bisa meraih 99% pengguna internet melalui komputer. HTML5 dan kawan2 memang menjanjikan, namun sepertinya masih belum tuk sekarang :)

Tagged , , | Leave a comment