做一個基於processing的影象序列處理儲存匯出的流程梳理。本案例沒有什麼實質性的目的,僅為流程梳理做演示。


準備

把需要處理的影像渲染成序列圖片,可以在PR中剪輯並匯出PNG序列【格式倒是沒什麼要求,看質量需求,Processing支援的格式都可以,詳情請參考這篇:Processing中PImage類和loadImage()、createImage()函式的相關解析】。

其中的命名規則也沒有什麼特殊要求,在Processing中都可以適應,如下圖:



OK!

編寫PDE

新建速寫本,然後儲存專案,把序列圖片塞進來,一般放在data資料夾中【PS:不放data也可以,採用絕對路徑讀取】。一切準備就緒。開始寫程式碼。

首先處理單張圖片。這裡就一併粘上:

PImage moviePicture;  //源影像截圖
PGraphics resultPicture; //處理過後的圖片 void settings(){
size(500, 500);
} void setup() {
moviePicture = loadImage("xqdz001.png"); //讀取
moviePicture.filter(GRAY); //灰階操作
resultPicture=createGraphics(moviePicture.width, moviePicture.height); //新建圖片
surface.setSize(moviePicture.width*2,moviePicture.height); //為了方便監視,重新分配視窗大小
//frameRate(24);
noLoop(); //因為是單張處理,不用迴圈
} void draw() {
resultPicture.beginDraw();
////////////////////////////////////////
// 這一塊是重點,核心演算法,很清晰的處理方式
// 即遍歷每個畫素,對比畫素資訊,然後填充給新的畫素塊
////////////////////////////////////////
for (int i = 0; i < width; ++i) {
for (int j = 0; j < height; ++j) {
color cc = moviePicture.get(i, j);
if (brightness(cc) > 200) { //如果亮度大於200 (區間 0 - 255)
resultPicture.set(i, j, cc);
} else {
color cl=color(0, 0, 0); //沒有達到亮度的以黑色填充
resultPicture.set(i, j, cl);
}
}
}
////////////////////////////////////////
resultPicture.endDraw(); image(moviePicture, 0, 0);
image(resultPicture,moviePicture.width,0);
// 有條件可以建立獨立視窗監視 resultPicture.save("result.png"); //匯出處理後的圖片
}

得到結果:



很顯然,我的做法是為了提取影像中最亮的畫素,即影片中光劍的內容以及各種反光。

接下來

修改程式碼,使之匹配處理多張圖片,即批處理。做法有很多,可以把loadImage讀取邏輯、影象處理、儲存等過程封裝成單獨一個個模組,也可以簡化一點,直接換字元讀取。

PImage moviePicture;  //源影像截圖
PGraphics resultPicture; //處理過後的圖片
int frame = 0; //幀數累計,方便得到圖片名字、讀取、儲存 void settings(){
size(500, 500);
} void setup() {
moviePicture = loadImage("xqdz"+nf(frame,3)+".png"); //讀取
moviePicture.filter(GRAY); //灰階操作
resultPicture=createGraphics(moviePicture.width, moviePicture.height); //新建圖片
surface.setSize(moviePicture.width*2,moviePicture.height); //為了方便監視,重新分配視窗大小
//frameRate(24);
//noLoop(); //因為要批處理了,所以把它關掉
} void draw() { resultPicture.beginDraw();
////////////////////////////////////////
// 這一塊是重點,核心演算法,很清晰的處理方式
// 即遍歷每個畫素,對比畫素資訊,然後填充給新的畫素塊
////////////////////////////////////////
for (int i = 0; i < width; ++i) {
for (int j = 0; j < height; ++j) {
color cc = moviePicture.get(i, j);
if (brightness(cc) > 200) {
resultPicture.set(i, j, cc);
} else {
color cl=color(0, 0, 0);
resultPicture.set(i, j, cl);
}
}
}
////////////////////////////////////////
resultPicture.endDraw(); image(moviePicture, 0, 0);
image(resultPicture,moviePicture.width,0);
// 有條件可以建立獨立視窗監視 resultPicture.save(dataPath("") + "\\result\\result"+ nf(frame,3)+".png"); //匯出處理後的圖片,路徑為data\result資料夾下
frame ++;
moviePicture = loadImage("xqdz"+nf(frame,3)+".png"); //讀取
moviePicture.filter(GRAY); //灰階操作
}

執行起來便得到結果:



如果你照搬我的寫法,哈哈,是有bug的!因為並沒有設定取值範圍,即超出幀數後,就讀不到圖片了,會報空指標異常如下:



不過也無所謂,因為這不需要實時執行看結果的,正好自己就結束了,哈哈~~~

正常的做法:

frame ++;
if(frame >= 600)
{
noLoop();
println("Finished!");
exit(); //退出程式
}

很簡單的邏輯,超出閾值讓它停止並結束。

延伸

上面的結果是不帶透明通道的。如果想要光留下高亮部分,其他部分沒有資訊,可以這麼來設定:

  resultPicture.beginDraw();
resultPicture.background(0,0); //每次重新整理圖片,注意`background`函式是可以帶alpha通道權重值引數的!
////////////////////////////////////////
// 這一塊是重點,核心演算法,很清晰的處理方式
// 即遍歷每個畫素,對比畫素資訊,然後填充給新的畫素塊
////////////////////////////////////////
for (int i = 0; i < width; ++i) {
for (int j = 0; j < height; ++j) {
color cc = moviePicture.get(i, j);
if (brightness(cc) > 200) {
resultPicture.set(i, j, cc);
} else {
color cl=color(0, 0, 0, 0); //不填充任何顏色資訊 ,此句可省略
resultPicture.set(i, j, cl);
}
}
}
////////////////////////////////////////
resultPicture.endDraw;

這樣得到的實時監視畫面如下:



得到的圖片如下:

結語

Processing處理影象是比較靈活的,沒有條條框框,隨心所欲。。。只要抓好幾個要點,即流程重點:

  1. 確保圖片物件存在並且Processing有權讀取
  2. 遍歷圖片畫素,計算處理,把新的結果輸出到新圖片上
  3. 儲存時注意通道的相關細節,還要注意路徑、命名等

其他的並沒有什麼難點。如果想要處理得理想,就得在畫素處理模組上下文章,學學圖形學,看看卷積、形態學、深度學習等知識!有需要補充的另開篇幅再總結,結束!