- Jun8
給 Nuke 寫了個 bmpReader
Drake on Tue, 2010-06-08 18:50因為不知名的原因,The Foundry 並沒有在 Nuke 裏頭支援 .BMP。雖然說,.BMP 是個一點都不專業,用了它,還可能讓對方把你瞧低了的格式,但很不幸地,我們因為一些使用到的程式,不得不去處理 .BMP。在這個情況下,我又希望能保有使用 Nuke 的便利與強大,但又不想三不五時就使用 ImageMagick 的 convert 來轉檔(太慢了,尤其是當圖檔有上千上萬張的時候)的前提下,我用 FreeImage 給 Nuke 寫了個 bmpReader。
希望對某些人,也有幫上一點唯不足道的忙~
#include <stdio.h> #include <string.h> #include <freeimage.h> #include "DDImage/DDWindows.h" #include "DDImage/Reader.h" #include "DDImage/Row.h" #include "DDImage/Knob.h" #include "DDImage/Knobs.h" #include "DDImage/Thread.h" #include "DDImage/Memory.h" using namespace DD::Image; class BmpReader : public Reader { private: unsigned int m_bpp; unsigned int m_width; unsigned int m_height; FIBITMAP* m_bitmap; FreeImageIO m_io; public: BmpReader(Read*); ~BmpReader(); void engine(int y, int x, int r, ChannelMask, Row&); static const Description d; }; BmpReader::BmpReader(Read* r) : Reader(r) { FreeImage_Initialise(TRUE); m_bpp = 0; m_width = 0; m_height = 0; m_bitmap = FreeImage_Load(FIF_BMP, filename(), BMP_DEFAULT); if( m_bitmap ) { m_bpp = FreeImage_GetBPP(m_bitmap); m_width = FreeImage_GetWidth(m_bitmap); m_height = FreeImage_GetHeight(m_bitmap); //printf("bpp: %d\n", m_bpp); //printf("width: %d\n", m_width); //printf("height: %d\n", m_height); set_info(m_width, m_height, m_bpp/8); } else { } } BmpReader::~BmpReader() { if( m_bitmap ) { FreeImage_Unload(m_bitmap); m_bitmap = NULL; } FreeImage_DeInitialise(); } void BmpReader::engine(int y, int x, int r, ChannelMask channels, Row& row) { BYTE* pixel = FreeImage_GetScanLine(m_bitmap, y); // BGR !!!! if( channels & Mask_Blue ) { from_byte(Chan_Blue, row.writable(Chan_Blue)+x, pixel+0, NULL, r-x, m_bpp/8); } if( channels & Mask_Green ) { from_byte(Chan_Green, row.writable(Chan_Green)+x, pixel+1, NULL, r-x, m_bpp/8); } if( channels & Mask_Red ) { from_byte(Chan_Red, row.writable(Chan_Red)+x, pixel+2, NULL, r-x, m_bpp/8); } } static Reader* build(Read* iop, int fd, const unsigned char* b, int n) { close(fd); return new BmpReader(iop); } static bool test(int fd, const unsigned char* block, int length) { FreeImage_Initialise(TRUE); FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; FIMEMORY* hmem = FreeImage_OpenMemory((BYTE *)block, length); fif = FreeImage_GetFileTypeFromMemory(hmem); FreeImage_DeInitialise(); return(fif == FIF_BMP); } const Reader::Description BmpReader::d("bmp\0", build, test); // vim: set hls is si et sw=4 sts=4 nu: - Jun8
bmpReader for Nuke
Drake on Tue, 2010-06-08 18:40For some reason I don't know, The Foundry doesn't make Nuke support .BMP's read/write. License issue? Anyway, we does have some files coming as .BMP such that I code this bmpReader with help of FreeImage. Hopefully, someone gets benefits from it.
#include <stdio.h> #include <string.h> #include <freeimage.h> #include "DDImage/DDWindows.h" #include "DDImage/Reader.h" #include "DDImage/Row.h" #include "DDImage/Knob.h" #include "DDImage/Knobs.h" #include "DDImage/Thread.h" #include "DDImage/Memory.h" using namespace DD::Image; class BmpReader : public Reader { private: unsigned int m_bpp; unsigned int m_width; unsigned int m_height; FIBITMAP* m_bitmap; FreeImageIO m_io; public: BmpReader(Read*); ~BmpReader(); void engine(int y, int x, int r, ChannelMask, Row&); static const Description d; }; BmpReader::BmpReader(Read* r) : Reader(r) { FreeImage_Initialise(TRUE); m_bpp = 0; m_width = 0; m_height = 0; m_bitmap = FreeImage_Load(FIF_BMP, filename(), BMP_DEFAULT); if( m_bitmap ) { m_bpp = FreeImage_GetBPP(m_bitmap); m_width = FreeImage_GetWidth(m_bitmap); m_height = FreeImage_GetHeight(m_bitmap); //printf("bpp: %d\n", m_bpp); //printf("width: %d\n", m_width); //printf("height: %d\n", m_height); set_info(m_width, m_height, m_bpp/8); } else { } } BmpReader::~BmpReader() { if( m_bitmap ) { FreeImage_Unload(m_bitmap); m_bitmap = NULL; } FreeImage_DeInitialise(); } void BmpReader::engine(int y, int x, int r, ChannelMask channels, Row& row) { BYTE* pixel = FreeImage_GetScanLine(m_bitmap, y); // BGR !!!! if( channels & Mask_Blue ) { from_byte(Chan_Blue, row.writable(Chan_Blue)+x, pixel+0, NULL, r-x, m_bpp/8); } if( channels & Mask_Green ) { from_byte(Chan_Green, row.writable(Chan_Green)+x, pixel+1, NULL, r-x, m_bpp/8); } if( channels & Mask_Red ) { from_byte(Chan_Red, row.writable(Chan_Red)+x, pixel+2, NULL, r-x, m_bpp/8); } } static Reader* build(Read* iop, int fd, const unsigned char* b, int n) { close(fd); return new BmpReader(iop); } static bool test(int fd, const unsigned char* block, int length) { FreeImage_Initialise(TRUE); FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; FIMEMORY* hmem = FreeImage_OpenMemory((BYTE *)block, length); fif = FreeImage_GetFileTypeFromMemory(hmem); FreeImage_DeInitialise(); return(fif == FIF_BMP); } const Reader::Description BmpReader::d("bmp\0", build, test); // vim: set hls is si et sw=4 sts=4 nu:- 留言回應
- 536 reads
- Chinese, Traditional
- Dec6
MPO2Stereo
Drake on Sun, 2009-12-06 11:20The following is the code I got from Andres Hernandez [cybereality] for separating a .mpo file into 2 relative .jpeg files (left and right). The key point is to find the second jpeg's header "FFD8FFE1" and this is my first time to touch jpeg's header format. Kinda interesting.
According to Andres's idea, we all hope everyone could keep sharing any informative stuff to help others' work or research. And eventually, we could benefit from others' great achievement :)
Cool, no problem man. It would be cool if you could upload the mac version on that same MTBS thread so it can help others. MTBS can host files, there is a small "browse for file" button under the post box, then click "add file", so it is very easy to include files in a thread. I am sure there are other mac users that would appreciate it.
Source: Meant to be Seen.// MPO2Stereo: // Converts Fujifilm MPO format 3D // images into JPEG stereo pairs // // code by: // Andres Hernandez [cybereality] #include <iostream> #include <fstream> using namespace std; int fileSize; char * stereoData; char * rightData; int main( int argc, char* argv[] ){ // title printf("\n*********** MPO2Stereo [cybereality] ***********\n"); printf("------------------------------------------------\n"); // print usage if no arguments if(argc == 1){ printf("Converts MPO files to JPEG stereo pairs.\n"); printf("Usage: MPO2Stereo.exe File_1.MPO [.. File_N.MPO]\n"); } // loop through each argument for( int i = 1; i < argc; i++ ){ // get the file name char fileName[255]; char fullName[255]; // locate end of path, which is the last slash in the string char *ptr = strrchr(argv[i],'/'); // make sure its a directory if(ptr != NULL){ // copy remainder of string strcpy(fileName,ptr+1); // remember full name strcpy(fullName, fileName); // check if its an MPO file char *MPO = strstr(fileName, ".MPO"); char *mpo = strstr(fileName, ".mpo"); // strip file extension ptr = strchr(fileName,'.'); // if the extension exists, truncate it if(ptr != 0) *ptr = 0; // make sure its an MPO file if(MPO == NULL && mpo == NULL){ printf("Cannot Process: \"%s\". Not An MPO File.\n", fullName); // go to next file if any continue; } else { // print file name printf("Processing MPO File: \"%s\"...\n", fullName); } } else { // malformed directory printf("Invalid Directory For File: %s\n", argv[i]); printf("Must Include Full Directory Path To MPO File.\n"); // skip continue; } // load up the MPO file ifstream mpoFile (argv[i], ios::in|ios::binary|ios::ate); // make sure we can open file if (mpoFile.is_open()) { // read the image data into a buffer fileSize = (int)mpoFile.tellg(); stereoData = new char [fileSize]; mpoFile.seekg (0, ios::beg); mpoFile.read (stereoData, fileSize); mpoFile.close(); // start of the next image char startOfImage [8]= { 'F', 'F', 'D', '8', 'F', 'F', 'E', '1' }; int imageBreak; // find the break point between the image pairs for(int i=0; i < fileSize; i+=4){ // get block into a c string char block[255]; // extract the characters sprintf(block, "%.2X%.2X%.2X%.2X", (unsigned int)(unsigned char)stereoData[i], (unsigned int)(unsigned char)stereoData[i+1], (unsigned int)(unsigned char)stereoData[i+2], (unsigned int)(unsigned char)stereoData[i+3]); // check for start of image if(strncmp(block, startOfImage, 8) == 0){ // remember where the image starts imageBreak = i; cout << imageBreak << endl; } } // write left image to new file char leftName[255]; sprintf(leftName, "%s_L.JPG", fileName); ofstream leftFile; leftFile.open (leftName, ios::out | ios::binary); if (leftFile.is_open()) { leftFile.write (stereoData, imageBreak); leftFile.close(); printf("Created JPEG File: \"%s\".\n", leftName); } else { printf("Unable To Create JPEG File: \"%s\".\n", leftName); } // write right image to new file char rightName[255]; sprintf(rightName, "%s_R.JPG", fileName); ofstream rightFile; rightFile.open (rightName, ios::out | ios::binary); if (rightFile.is_open()) { rightFile.write (stereoData + imageBreak, fileSize-imageBreak); rightFile.close(); printf("Created JPEG File: \"%s\".\n", rightName); } else { printf("Unable To Create JPEG File: \"%s\".\n", rightName); } // delete resources delete[] stereoData; } else { printf("Unable To Open MPO File.\n"); } } // all done printf("------------------------------------------------\n"); // return return 0; }- 4 則回應
- 1686 reads
- Mar5
How to clean up "Render Stats" of all geometry in Maya?
Drake on Thu, 2009-03-05 16:12No matter what renderer you are going to use, you should take care of geometry's "Render Stats" before rendering, especially when you are planning to decompose a rendering job into several layers/passes. Here is a quick python scripts based on pymel to do that.
from pymel import * def dgVerifyRenderStats(surfaces=None): ''' dgVerifyRenderStats deps: none verify and adjust all surface's render stats. double sided, opposite, smooth shading, motion blur visible in reflections/refractions, casts/receive shadows primary visibility ''' def _verifier(surface): surface.doubleSided.set(0) surface.opposite.set(0) surface.smoothShading.set(1) surface.motionBlur.set(1) surface.visibleInReflections.set(1) surface.visibleInRefractions.set(1) surface.castsShadows.set(1) surface.receiveShadows.set(1) surface.primaryVisibility.set(1) # save the selection selections = ls(selection=True) if surfaces is None: surfaces = ls(long=True, type='surfaceShape') map(_verifier, surfaces) # restore the selection select(selections, r=True)- 留言回應
- 3571 reads
- Feb28
How to make Maya output all messages into console/terminal?
Drake on Sat, 2009-02-28 15:12
Recently, Timothy and I were talking about some interesting about message in Maya.For daily tasks in Digimax, Timothy has to handle a mass of Maya scene files in batch way such that he can make it on schedule. But there are so many tasks and Maya scene files! He has spent some time preparing several scripts (Mel or Python, but most of them are Python) for that task. It seems work well but sometimes, he needs to get output messages from Maya for debugging and other scripts. For example, one Python script when invoked inside Maya would output a list of filtered and managed nodes in one Maya scene and after that, we can apply some other programs/scripts on nodes by this message.
Question is:
How could we redirect all Maya messages into console or terminal while Maya is invoked in GUI mode?
My first reaction was "What a weird and kinda stupid question?" 'cause it seems to be non sense to have a GUI mode Maya redirect its message to console. Because you have already opened a Maya GUI, all you have to do can be done within that GUI though. But it turns to to make sense when you are going to make use of scripts to automatically handle lots of Maya scenes (ex, 100 animation shots, each has more than one Maya scene files!). If this was the case, why did we need the GUI? How about invoking Maya as standalone mode and in that way, we all output messages were naturally redirected to console!
"Some scripts/nodes need GUI's redrawing to make it re-calculate values, such that we need Maya in GUI mode.", replied by Timothy. Well, IMHO, there should be some way to do that Dependency node's evaluation in standalone mode but we didn't know that yet. Actually, there is a likely hot topic "forcing a custom node's attribute to compute every dep graph update" in Python Inside Maya group but let's just skip it and figure out some hacking way to redirect all messages to console/terminal.
Problem Definition:
- Maya is invoked from a terminal/console as a GUI mode. All Maya messages have to be correctly redirected to the console.
- There are 3 types of messages: 1) msgs by Maya itself, 2) msgs from Mel's print and 3) msgs from Python's print.
Solution:
- We can make use of sys.__stdout__, the default standard output (terminal) to redirect Python's print.
- For Mel's print and Maya's command messages, we need to use CommandMessage.CommandOutputCallback in Maya API. Just register a CommandOutput callback and everytime there is a message, this callback would be invoked.
- 2 則回應
- 閱讀全文
- 4595 reads
- Chinese, Traditional
- Feb24
如何讓 Maya 裏的所有 message 都丟到 console 外頭?
Drake on Tue, 2009-02-24 00:22他因為工作需要,常常要批次(batch)處理一大堆的 Maya scene files,透過一些他自行寫的 scripts 來執行一些例行性的工作。然後他需要把 Maya 產生的任何 message 都以某種型式丟出來,方便他 debug 或是分析。我們兩個人在想,有哪些方法可以做到?(從這點可以看出來,Timothy 是一位非常稱職的 RD/TD,會汲汲於想一些方法來做 mass production,而不是手動一個一個處理。因為即使你手動做得再快,只要量一大,還是有一定的速度上限~)
我們最後釐清了需求:
- Maya 會由 terminal/console 下執行,所有的 message 要能正常輸出,而不是只留在 Maya Script Editor 裏頭。
- message 包括:Maya 本身產生的;Mel 的 print;Python 的 print。
解法:
- 要讓 Python 的 print 得以輸出到 terminal,只要把 sys.stdout 由原來的 Maya Output object 換成 sys.__stdout__ 即可。
- 要讓 Mel 的 print 與 Maya 原生的 message 輸出到 terminal 的話,需要利用到 Maya API 裏頭 CommandMessage 所提供的 CommandOutputCallback。向 Maya 註冊一個 callback 後,每當有任何一個 command 的 output 時,這個 callback 就會被呼叫,然後我們可以來決定怎麼處理這個 output message。
- Feb4
pymel 讓 optionVar 用起來看起來,更像 dictionary
Drake on Wed, 2009-02-04 13:15Maya 裏頭有個非常 high level 的 mel function: optionVar,有別於其它的 functions 的簡單與立即反應,它的目的在提供一個跟 Maya session 的設定檔的存取。換個說法,你可以在一次執行 Maya 的時候,存一個自訂的變數 IamSoHandsome,然後在下一次執行時取出來用。所以你可以把它想成是 Windows 上頭的 registry、Unix-like 下的 .ooxx 個人設定檔、或是會永續存在下去的 environment variables。
optionVar 另一個優秀的地方在於,它允許你隨時更改存下來的變數的 data type。所以你可以今天把 IamSoHandsome 宣告成一個只有 0 與 1 的 boolean,然後明天改成 "yes" 與 "no" 的 string,只要直接指定給它一個新的 data type 就做到了。如下:
optionVar -iv "IamSoHandsome" 1; ... ... optionVar -sv "IamSoHandsome" "yes"; ... ...optionVar -sv:
creates a new variable named using the first string with value given by the second string. If a variable already exists with this name, it is overridden in favour of the new value (even if the type is different).pymel 更酷了,它讓整個 optionVar 變成了一個 dictionary-like global object(singleton class),所以你可以非常迅速地做到如下幾件事:
print optionVar["RecentFilesList"] myVar = optionVar.get("IamSoHandsome", 1) # default to 1 if it is not in optionVar optionVar["IamSoHandsome"] = 1 optionVar["IamSoHandsome"] = "yes" optionVar["IamSoHandsome"] = [1, 2, 3, 4] # quickly change its value and data type without any hesitate optionVar["IamSoHandsome"] = optionVar["IamSoHandsome"][1:] # [1,2,3,4] => [2,3,4]我愈來愈愛用 optionVar 來提供更好的 maya GUI scripts 了 :)
- 留言回應
- 1788 reads
- Feb4
Maya UI 裏的 Callback
Drake on Wed, 2009-02-04 00:39GUI 是個非常偉大的發明,它讓電腦就這麼快速地滲透進家家戶戶與各大小商辦或公家機關,因為我們有 Mac OS、Windows、X Window System,再加上現下當紅的掌上型界面(iPhone、NDSL、PDS)與從 web browser 裏出生的 web interface,世界就此進入資訊高科技時代,人人都會使用這些電腦設備,因為它們都提供了一套容易使用的 GUI。
我依稀記得我的第一個有 GUI 的程式,是在新竹中學的軟研社(22th招生影片)裏頭,使用 QuickBasic 呼叫底層的 VGA interrupt 來存取 video buffer,在螢幕上畫了兩個眼睛,裏頭的眼珠子會依據滑鼠游標所在的方位,往那對應的方向看過去。這隻程式除了滑鼠移動時所造成的 event 以外,就沒別的了,既沒有 button 也沒有 textField 供輸入什麼的,非常的陽春,但我寫得很愉快,這經驗一直是那麼的深刻。
時間拉回到進入太極影音以降。在 Maya 裏頭 scripts 提供 GUI programming 的函式,算是進入「MFC 之前,event driven 之後」的時代。所以 Maya 提供的 control(ex, button)大都可以針對一些 event(ex, click)指定一個 callback ,寫這類的程式對於一般修讀過四年資訊課程的學生來說,不是什麼問題。
遺憾的是,Maya 的 callback 機制很原始,callback 裏頭的程式碼並不會繼承所在的 namespace(or scope)的資訊,而是在 global namespace 下執行的,拿下頭這個例子來看:
proc dgCallbackCancel() { deleteUI "ooxx"; } global proc dgShiftAnimationKeysGUI() { if(`window -exists "ooxx"`) { deleteUI "ooxx"; } string $win = `window -title "ooxx by Drake" "ooxx"`; rowColumnLayout -nc 2 -cal 1 "right" -cal 2 "left" -cw 1 100 -cw 2 200 -w 300; text -label "Mode : "; string $mode = `radioButtonGrp -nrb 2 -labelArray2 "All" "Selected" -cw2 80 80 -select 1 "mode"`; text -label "Frames to shift : "; string $shift = `textField "shift"`; button -label "Do It" -c "dgCallbackDoIt()"; button -label "Cancel" -c "dgCallbackCancel()"; showWindow(); } dgShiftAnimationKeysGUI();兩個 button 的個別 callback functions 裏頭,為了可以順利存取到其它 control,使用了 hard-coded control name 的方式(直接假設 window name 是 "ooxx", 另外兩個 control 分別是 "mode" 與 "shift"),另一種作法是使用全域變數的方式,直接把這些 control 的名稱存到全域變數,然後在 callback functions 裏頭使用他們。
透過 pymel (0.7.8) 的包裝,加上 python 動態執行時仍擁有 local scope variables 的特性,可以既不用寫死 control name(寫死也沒關係,但已經沒有這個必要了),也不需要使用到全域變數,就可以做同與上頭一樣的事。
from pymel import * def dgCallbackCancel(win): # just delete the window win.delete() def dgCallbackDoIt(win, shift, mode): # do something and then delete the window # ... modeValue = mode.getSelect() shiftValue = shift.getText() win.delete() def dgShiftAnimationKeysGUI(): winName = "ooxx" if window(winName, exists=1): deleteUI(winName, window=1) if windowPref(winName, exists=1): windowPref(winName, remove=1) win = window(winName, title="ooxx by Drake") rowColumnLayout(nc=2, cal=[(1, "right"), (2, "left")], cw=[(1, 100), (2, 200)], w=300) text("Mode : ") mode = radioButtonGrp(nrb=2, labelArray2=["All", "Selected"], cw2=[80, 80]) text("Frames to shift : ") shift = textField() button("Do It", align="center", c=lambda *args: dgCallbackDoIt(win, shift, mode)) button("Cancel", align="center", c=lambda *args: dgCallbackCancel(win)) showWindow(win) dgShiftAnimationKeysGUI()請仔細看 button 那兩行的最後。pymel(0.7.8)建議使用 lambda function 來包裝,然後依自己的需要把 local variables 給傳進 callback function 裏頭。所以在這個例子裏題頭,我可以把 window 與兩個 control(mode, shift)直接傳給 callback function,而不用去理會任何細節,同時很意外地減少了 programming side-effect。
pymel(0.7.8)對於 Maya GUI control 的包裝還非常的陽春,雖然每個 UI control 都包成一個 class,但用起來與直接呼叫 mel 無異,並沒有提供多少的抽像與方便的設計。舉個最明顯的例子:當你要對任一個 control 取值時,你得記得是使用 getText()、getSelect()、getLabel()、getValue()、getMaxValue()…多少有點強制要求 programmer 要熟習 mel UI programming 那一套,或是得很勤勞地查閱 api 說明文件或是利用 pydoc 之類的。但我想這一點在未來的發展會獲得改善。不過我也意外注意到,這問頭不管是古老的 MFC 還是 wxWidgets, Qt,也都一樣有這類的問題就是。我總覺得,讓使用者愈少去猜或計要呼叫哪個 member function,就是一個很好的設計 :p
- Dec30
Delete unknown nodes in Maya
Drake on Tue, 2008-12-30 17:10Sometimes you need to delete unknown nodes in a Maya scene. You can do it in the following python snippet.
from pymel import * def deleteUnknownNodes(): # 2 things to take care of: # 1) you can't delete nodes from references. # 2) "delete" will delete all children nodes (hierarchy) in default. unknown = ls(type="unknown") unknown = filter(lambda node: not node.isReferenced(), unknown) for node in unknown: if not objExists(node): continue delete(node)Compare it to the MEL way:
proc dgSaveDeleteUnknown() { string $unknown[] = `ls -type "unknown"`; if( size($unknown) ) { int $i; for($i=0; $i<size($unknown); $i++) { if(!`objExists $unknown[$i]`) continue; if(!`referenceQuery -inr $unknown[$i]`) delete $unknown[$i]; } } }But why do we need to do that? I mean, why do we need to delete unknown nodes and where do those nodes come from? In theory, there should be no unknown nodes in a Maya scene file!
The answer is: Yes, you are right but if you are in an animation studio with complicated animation pipeline because animation is a vivid industry, it would happen to you!
Almost all unknown nodes come from 3rd-party Maya plugins. Let's say, your colleagues used a magical Maya plugin (ex, Realflow) such that there are some RealFlowParticlerGetMaxRange, RealflowEmitter or RealflowMesh nodes in the Maya scene. You were the downstream of this Maya scene and unfortunately, your Maya didn't know how to negotiate with those nodes (ex, you didn't have the Realflow plugin or you forgot to load it), those nodes became unknown to Maya. You could delete those nodes if they were not contributing anything to your workflow. (Ex, you are in lighting department and the rendering of fluid simulation is not your business.)
- 留言回應
- 3052 reads
- Dec29
Selecting multiple appearances in slim with MEL
Drake on Mon, 2008-12-29 16:55slimcmd "plt0 SelectAppearances {func0 func1 func2}"this might failed....!!! use the following.
slimcmd plt0 SelectAppearances "{func0 func1 func2}"- 留言回應
- 2264 reads
