Artikel ini menyajikan implementasi algoritma Python untuk mengenali sumber cahaya pada peta lingkungan (LDR atau HDR) menggunakan proyeksi equirectangular. Namun, setelah melakukan perubahan kecil, itu juga dapat digunakan dengan gambar latar belakang sederhana atau peta kubik. Contoh penerapan algoritma yang mungkin: program penelusuran sinar di mana diperlukan untuk mengenali sumber cahaya primer agar dapat memancarkan sinar darinya; pada penyaji raster, ini dapat digunakan untuk melemparkan bayangan menggunakan peta lingkungan; selain itu, algoritme juga dapat digunakan dalam program penghapusan sorotan, misalnya dalam AR.
Algoritme terdiri dari langkah-langkah berikut:
- Penurunan resolusi gambar asli, misalnya, ke 1024.
- Konversikan gambar menjadi kecerahan (luminance), jika perlu, dengan gambar buram.
- Penerapan metode quasi-Monte Carlo.
- Transformasi dari koordinat bola ke koordinat yang sama.
- Memfilter sampel berdasarkan kecerahan tetangga.
- Sortir sampel berdasarkan kecerahannya.
- Memfilter sampel berdasarkan metrik Euclidean.
- Menggabungkan sampel menggunakan algoritma Bresenham.
- Perhitungan posisi cluster pencahayaan berdasarkan kecerahannya.
Ada banyak algoritma untuk mengurangi resolusi gambar. Pemfilteran bilinear adalah yang tercepat atau termudah untuk diimplementasikan, dan selain itu, paling cocok dalam banyak kasus. Untuk mengonversi kecerahan di gambar LDR dan HDR, Anda dapat menggunakan rumus standar:
lum = img[:, :, 0] * 0.2126 + img[:, :, 1] * 0.7152 + img[:, :, 2] * 0.0722
Selain itu, Anda dapat menerapkan sedikit kekaburan pada gambar kecerahan, misalnya, 1-2 piksel untuk gambar dengan resolusi 1024, untuk menghilangkan semua detail frekuensi tinggi (khususnya, yang disebabkan oleh penurunan resolusi).
Proyeksi yang Sama
Proyeksi yang paling umum di peta lingkungan adalah proyeksi yang sama rata
3 . Algoritme saya dapat bekerja dengan proyeksi lain, misalnya, dengan panorama dan peta kubik, namun, dalam artikel ini kami hanya akan mempertimbangkan proyeksi dengan jarak yang sama. Pertama, Anda perlu menormalkan koordinat gambar:
pos[0] = x / width pos[1] = y / height
Maka kita perlu mengkonversi dari dan ke koordinat Cartesian menggunakan koordinat bola, mis. ฮธ dan ฯ, di mana ฮธ = x * 2ฯ, dan ฯ = y * ฯ.
def sphereToEquirectangular(pos): invAngles = (0.1591, 0.3183) xy = (math.atan2(pos[1], pos[0]), math.asin(pos[2])) xy = (xy[0] * invAngles[0], xy[1] * invAngles[1]) return (xy[0] + 0.5, xy[1] + 0.5) def equirectangularToSphere(pos): angles = (1 / 0.1591, 1 / 0.3183) thetaPhi = (pos[0] - 0.5, pos[1] - 0.5) thetaPhi = (thetaPhi[0] * angles[0], thetaPhi[1] * angles[1]) length = math.cos(thetaPhi[1]) return (math.cos(thetaPhi[0]) * length, math.sin(thetaPhi[0]) * length, math.sin(thetaPhi[1]))
Hammersley Sampling
Langkah selanjutnya adalah menerapkan metode quasi-Monte Carlo, misalnya, sampel Hammersley
2 :
Anda dapat menggunakan metode pengambilan sampel lainnya, seperti Holton
4 , tetapi Hammersley lebih cepat dan memberikan distribusi sampel yang baik di seluruh bidang. Holton akan menjadi pilihan yang baik untuk sampel bidang jika gambar sederhana digunakan sebagai pengganti peta lingkungan. Persyaratan wajib untuk pengambilan sampel Hammersley adalah pembalikan akar (baris) van der Corpute, untuk lebih jelasnya lihat tautan
2 . Berikut ini adalah implementasinya yang cepat:
def vdcSequence(bits): bits = (bits << 16) | (bits >> 16) bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1) bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2) bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4) bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8) return float(bits) * 2.3283064365386963e-10
Lalu kami menggunakan overlay seragam di bola:
def sphereSample(u, v): PI = 3.14159265358979 phi = v * 2.0 * PI cosTheta = 2.0 * u - 1.0
Untuk mengambil sampel Hammersley, kami menggunakan jumlah sampel tetap, tergantung pada resolusi gambar, dan mengkonversi dari koordinat bola ke Cartesian, dan kemudian ke jarak yang sama:
samplesMultiplier = 0.006 samples = int(samplesMultiplier * width * height) samplesList = []
Ini akan memberi kami distribusi sampel yang baik yang akan diperiksa keberadaan sumber cahaya:
Memfilter sumber cahaya
Pada penyaringan pertama, kami mengabaikan semua sampel yang tidak melebihi ambang kecerahan (untuk kartu HDR, mungkin lebih tinggi), dan kemudian mengurutkan semua sampel berdasarkan kecerahannya:
localSize = int(float(12) * (width / 1024.0)) + 1 samplesList = []
Lulus berikutnya akan melakukan penyaringan berdasarkan metrik Euclidean dan jarak ambang antara piksel (tergantung pada resolusi gambar) - ini adalah struktur data spasial yang dapat digunakan untuk menghilangkan kompleksitas O (N
2 ):
euclideanThreshold = int(float(euclideanThresholdPixel) * (width / 2048.0))
Sampel yang dihasilkan melewati tahap penggabungan untuk lebih mengurangi jumlah sumber cahaya:
Menggabungkan sumber cahaya
Pada tahap terakhir, penggabungan sampel milik cluster pencahayaan yang sama dilakukan. Untuk melakukan ini, kita dapat menggunakan algoritma Bresenham dan mulai dengan sampel dengan kecerahan tertinggi, karena sudah dipesan. Ketika kami menemukan sumber cahaya yang memenuhi tes Bresenham, kami menggunakan posisinya untuk mengubah posisi sumber berdasarkan pada bobot pelarian:
Fungsi Bresenham memeriksa garis kontinu yang memiliki kecerahan yang sama. Jika delta dalam piksel saat ini melebihi batas tertentu, maka pemeriksaan gagal:
def bresenhamCheck(lum, imageSize, x0, y0, x1, y1): dX = int(x1 - x0) stepX = int((dX > 0) - (dX < 0)) dX = abs(dX) << 1 dY = int(y1 - y0) stepY = int((dY > 0) - (dY < 0)) dY = abs(dY) << 1 luminanceThreshold = 0.15 prevLum = lum[x0][y0] sumLum = 0.0 c = 0 if (dX >= dY):
Perlu dicatat bahwa, jika perlu, perbaikan dapat dilakukan pada uji Bresenham, yang akan mengarah pada penggabungan sampel yang lebih baik, misalnya, dapat memperhitungkan transfer horizontal sumber cahaya yang terletak di tepi gambar. Selain itu, fungsinya dapat dengan mudah diperluas sehingga mendekati area sumber cahaya. Peningkatan lainnya: Anda bisa menambahkan ambang jarak sehingga Anda tidak menggabungkan sampel yang terlalu jauh. Hasil akhir:
Biru menunjukkan maksimum lokal dari cluster pencahayaan, biru menunjukkan posisi akhir sumber cahaya, dan merah menunjukkan sampel yang merupakan bagian dari cluster pencahayaan yang sama dan dihubungkan oleh garis.
Contoh hasil lainnya:
- Deteksi sumber cahaya dalam foto digital oleh Maciej Laskowski
- Poin Hammersley di Belahan Bumi oleh Holger Dammertz
- Proyeksi Equirectangular oleh Paul Reed
- Sampling dengan Poin Hammersley dan Halton oleh Tien-Tsin Wong