2013年12月11日 星期三

OpenCL 模擬 iOS7 毛玻璃效果(frosted glass)

最近筆者將手機作業系統升級到 iOS7, 發現 iOS7 的介面有一個亮點 : 毛玻璃(frosted glass)效果, 透過毛玻璃所在的 layer, 可以隱約看到下層 layer 的內容. 如圖一所示.

圖一
搜尋一些資訊後發現 不少人在討論這個效果, 因為 apple 使用的方法無法確切得知, 所以筆者就以自己的想法實作.  首先 需要一個用以柔化影像的 low-pass filter. 筆者計畫使用 Gaussian filter 並且使用 GPU 做 Gaussian filter convolution. 關於 Gaussian filter, 可以參考下面兩個在 wiki 的介紹:
Gaussian kernel 的大小由 sigma 決定, Host 端的程式根據 sigma 決定 kernel size, 根據 gaussian filter 的定義計算 mask 中的每個常數, 將 mask 儲存在 OpenCL 的 image object.    kernel size 以及 mask 會給 OpenCL kernel 進行 convolution 時使用. 用以計算 Gaussian mask 的程式片段如下:

void
CreateBlurMask(float sigma, int * maskSizePointer) 
{
int maskSize = (int)ceil(3.0f*sigma);
g_mask.resize((maskSize*2+1)*(maskSize*2+1));

float sum = 0.0f;
float* pmask = &g_mask[0];
pmask += maskSize*(maskSize*2+1) + maskSize;

for(int a = -maskSize; a < maskSize+1; a++)
{
for(int b = -maskSize; b < maskSize+1; b++) 
{
float temp = exp(-((float)(a*a+b*b) / (2*sigma*sigma)));
sum += temp;
pmask[a+b*(maskSize*2+1)] = temp;
}
}
// Normalize the mask
for(int i = 0; i < (maskSize*2+1)*(maskSize*2+1); i++)
g_mask[i] = g_mask[i] / sum;

*maskSizePointer = maskSize;
}

根據我前一篇文章 OpenCL介紹 作修改, 在 host 端程式代碼的初始化, 大致上增加了以下的部分.
調用 CreateBlurMask, 並且給定 sigma = 3.0

CreateBlurMask(3.0, &g_maskSize);

接著, 建立一個 image object, 並且將 Gaussian mask 的內容由 host 端的 memory 複製到 device 端的 memory:
imgObject[2] = clCreateImage2D(GPU_context, CL_MEM_READ_ONLY,                                                                  &mask_fmt, g_maskSize*2+1, 
                                                       g_maskSize*2+1, 0, NULL, NULL);

err = clEnqueueWriteImage(commandQueue, imgObject[2], CL_TRUE, origin, 
                                         mask_region, 0, 0, &g_mask[0], 0, NULL, NULL);

if(err != CL_SUCCESS) 
{
cleanUpOpenCL(GPU_context, commandQueue, program, kernel, 
         memoryObjects, numberOfMemoryObjects,
 imgObject, numberOfImgObjects);
return 0;

}

設定好送進 kernel function 的參數
clSetKernelArg(kernel, 2, sizeof(cl_mem), &imgObject[2]);  // source image
clSetKernelArg(kernel, 3, sizeof(cl_int), &g_maskSize);       //  mask Size


Host 端處理每個 frame 的流程如下:
(1) 產生一份原始影像 scaling down 成較低解析度後的結果.
(2) 讓GPU對 較低解析度的影像 進行 convolution.
(3) 讀回結果.
(4) CPU 將讀回的結果scaling up 成和原始影像一樣解析度.
(5) CPU 根據 ROI,  將scaling up後的影像和原始影像合成.

圖二

因為要 composition, 故需要產生一份 composition 使用的 mask. 此mask 在 ROI 內的值為 0xff,其餘部分皆為 0.

camera >> Img1;
Mat mask(Img1.rows, Img1.cols, CV_8UC1);
mask.setTo(0);

Mat roi(mask, Rect(140,100,340,260));
roi = 0xff;

composition 的代碼如下, ocl_img 是從讀回讀回並 scaling up 的結果. Img0 則是原始影像.
ocl_img *= 0.3;
ocl_img.copyTo( Img0, mask );

接下來看 Kernel Function 的內容:
__constant sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |
                               CLK_ADDRESS_CLAMP | 
                               CLK_FILTER_NEAREST;

__kernel void laplacian( read_only  image2d_t src_image,                         
                         write_only image2d_t dst_image,
                         read_only  image2d_t mask,
                         __private int maskSize )
{
int x = get_global_id(0);
int y = get_global_id(1);

int2 pos = {get_global_id(0), get_global_id(1)};


    // Collect neighbor values and multiply with Gaussian
    float4 fSum  = 0.0f;
    float4 fMask;
    
    float4 fColor;
    uint4  uiColor;    
    
    for(int a = -maskSize; a < maskSize+1; a++) 
    {
        for(int b = -maskSize; b < maskSize+1; b++) 
        {
            int2 coord = (int2)((a+maskSize, b+maskSize));
            uiColor = read_imageui(src_image, sampler, pos + (int2)(a,b));
            fColor  = convert_float4(uiColor);
            
            fMask   = read_imagef(mask, sampler, coord);
            
            fSum += fColor * fMask.x;
        }
    }
       
    uiColor = convert_uint4 (fSum);   
    write_imageui(dst_image, pos, uiColor);    
}
Kernel 這邊的代碼 根據 mask size 進行 convolution, 再將結果寫到 output image object.

2013年11月12日 星期二

OpenCL 介紹

前言

在以往只有 fixed function pipeline 的時代, GPU 單純做為 3D 繪圖加速器, 應用程式透過諸如 OpenGL, OpenGL ES, Direct3D 等等的 3D API, 使用硬體提供的繪圖加速服務, 在 programmable pipeline 出現之後, GPU的應用進入了另一個領域 : computing. 這個階段出現了一個新的名詞 : GPGPU; General Purpose Computing on Graphics Processing Unit. GPU 內的 shader, texturing pipeline, arithmetic pipeline以及 Multi-thread 架構, 開始被應用在計算領域. 在這個階段, 計算應用必須使用標準的 3D API來完成, 這對計算應用在實作上造成不便, 例如, 要實做一個簡單的浮點數資料相加計算程式, 需要涉及到像是 vertex data, texture 等 和計算無關的資料, 概念和流程.
為了讓 GPU 更方便使用在計算領域, 專門為 computing 而制定的 API 出現了,
這讓 GPU 在 computing 的應用進入了第二個階段, 這個階段的概念是 Heterogeneous Computing; 異質性計算, 要能使用系統上不同性質的運算單元, 舉例來說, 在 CPU與 GPU 的協同運算中, 由CPU 負責程式的流程控制, 大量的資料則交由 GPU 以平行化的方式進行計算.
常見的應用包括:
image processing
computer vision
speech processing

可以參考筆者另一篇文章 OpenCL 毛玻璃效果

目前業界主要的 compute API 有以下四種:
(1) OpenCL ; Apple 首先提出, 並聯合多家廠商合組 working group, 目前由 Khronos 維護. 是本文章要介紹的API.
(2)C++AMP, DirectCompute ; 由 MicroSoft 主導制定.
(3)CUDA ; nVidia 自家的規格.
(4)Renderscript ; Google 制定, Android 3.0 開始出現.

其實使用這些 API 的目的不外乎就是要藉 GPU 的平行運算能力, 縮短計算任務所需的時間.
因此以平行化運算的方式思考, 應該是使用 GPU 加速運算的重點.
Laplace filter 影像處理














2013年8月23日 星期五

在程式中開關 log 的方法

撰寫程式, 不論是為了除錯, 或是了解程式運作的狀態, 需要在程式代碼中 加進一些 log.
一旦加入 log, 當然也需要一個開啟和關閉 log 的機制. 而且必須是一個方便使用的開關機制.

一般來說, 我們可能會使用 以下的方式在程式代碼中加入 log

  #define MY_DEBUG
  .......

  #ifdef  MY_DEBUG 
  printf("This is a log.\n");
  #endif
  .......

使用 MY_DEBUG 控制 log 機制的開啟和關閉.
這種做法的缺點是 加入 log 的同時也必須跟著加入 #ifdef 和 #endif. 撰寫上不方便 且程式代碼會看起來比較凌亂.

2013年8月15日 星期四

Linux PID 何時會被重複使用

在 Android 中, 大部分的 app在執行時, 若是使用者按下 back 鍵 回到桌面, 該 app 的 process 會暫停執行, 但是 process 可能仍然會存在系統中. 如圖一所示是尚未執行 app 前的系統 process 狀態:
圖一 未執行app 之前系統中的 process

2013年2月5日 星期二

OpenGL projection matrix

OpenGL projection matrix

      

       為了將 3D 場景呈現在 2D 的顯示裝置, 必須透過 projection transform 3D 座標 轉換成 2D 座標, 不論是 OpenGL ES 1.1 fixed function pipeline或是 OpenGL ES 2.0/3.0 vertex shader, 都需要使用投影矩陣(projection matrix)來進行 projection transform. 根據投影方式Projection matrix 分為兩種, 分別是透視投影 (perspective projection) 以及平行投影 (parallel projection), 本文說明如何推導 OpenGL ES perspective projection matrix.


2013年1月29日 星期二

攝像頭校正 camera calibration - part3 OpenCV programming

攝像頭校正 camera calibration - part3 OpenCV programming

在前兩篇文章  camera calibration -part1 camera model 以及 camera calibration -part2 camera calibration 中介紹了 camera 內部參數(intrinsic parameter) , 形變參數 (distortion parameter) 以及校正的方法和原理, 本文說明如何使用 OpenCV 實作camera校正 程序.
校正的程序分為兩部分;