做一個基於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處理圖像是比較靈活的,沒有條條框框,隨心所欲。。。只要抓好幾個要點,即流程重點:
- 確保圖片對象存在並且Processing有權讀取
- 遍歷圖片像素,計算處理,把新的結果輸出到新圖片上
- 保存時注意通道的相關細節,還要注意路徑、命名等
其他的並沒有什么難點。如果想要處理得理想,就得在像素處理模塊上下文章,學學圖形學,看看卷積、形態學、深度學習等知識!有需要補充的另開篇幅再總結,結束!
