Stumbling Toward 'Awesomeness'

A Technical Art Blog

Sunday, August 8, 2010

Sigma 8mm vs 4.5mm Comparison on Nikon APS-C

click to enlarge

I have been researching the best options available for the D300 when it comes to quickly generating some lightprobes/panoramas. This of course means fisheye lenses. Currently, Sigma is the only company that makes a 180 degree circular fisheye. They come in two flavors, 8mm, and 4.5mm. The 8mm projects a full circle onto a full 35mm sensor (full frame), but on an APS-C sensor it is cropped. The 4.5mm however, throws a perfect circular image onto an APS-C sized sensor; I believe it is the only lens that does this.

The Pixels

You would think that the 4.5mm would be the way to go, I did until I took a look at both. It really comes down to the pixels. The width in pixels of the image thrown by the 4.5mm lens is roughly 2285px in diameter. So while you can shoot less, an entire panorama taking about 3 shots, it will come out as a <4k equirectangular. However, using the 8mm, you need 4 shots, plus one zenith (5 shots total) and it generates an 8k image.  While the 4.5mm does generate a 180 degree image across, as you can see it is very wasteful.

So why doesn’t the lens have full coverage in at least the short dimension? I think it’s because it’s a lens designed to fit Canon and Sigma cameras, not just Nikon. Canon sensors have a 1.6 crop factor and Sigma’s Foveon X3 has a 1.7 crop factor (13.8mm)! The coverage is so small because Nikon DX format has a 1.5 crop factor, the APS-C sensor is much larger than Canon or Sigma. The actual circle measures 12.3mm, even small for the Sigma, which makes me believe they future-proofed it for Four Thirds.

For an APS-C sensor like the D300, I would recommend the 8mm, unless you really need a full uncropped image. The 4.5mm, while being more expensive, also has an aperture of 2.8, compared to the 8mm (f/3.5)

I am not super constrained on time, if you are on set and shooting bracketed probes between takes or something, the 4.5mm will save you two shots (18 pictures) and this might be preferable. That said, it will only generate a 4k image in the end (which might be enough)

posted by admin at 2:56 pm  

Wednesday, July 8, 2009

Buggy Camera Issues In Maya on x64

Many, many people are having weird, buggy camera issues where you rotate a view and it snaps back to the pre-tumbled state (view does not update properly). There are posts all over, and Autodesk’s official response is “Consumer gaming videocards are not supported”. Really? That’s basically saying: All consumer video cards, gaming or not, are unsupported. I have had this issue on my laptop, which is surely not a ‘gaming’ machine. Autodesk says the ‘fix’ is to upgrade to an expensive pro-level video card. But what they maybe would tell you if they weren’t partnered with nVidia is: It’s an easy fix!

Find your Maya ENV file:

C:\Documents and Settings\Administrator\My Documents\maya\2009-x64\Maya.env

And add this environment variable to it:

MAYA_GEFORCE_SKIP_OVERLAY=1

Autodesk buried this information in their Maya 2009 Late Breaking Release Notes, and it fixes the issue completely! However, even on their official forum, Autodesk employees and moderators reply to these draw errors as follows:

Maya 2009 was tested with a finite number of graphics cards from ATI and Nvidia, with drivers from each vendor that provided the best performance, with the least amount of issues. (at the time of product launch).  A list of officially qualified hardware can be found here: http://www.autodesk.com/maya-hardware. Maya is not qualified/supported on consumer gaming cards.  Geforce card users can expect to have issues.  This is clearly stated in the official qualification charts mentioned above.

posted by admin at 10:43 am  

Sunday, November 30, 2008

24″+ Monitor Panels in One Easy Table (TN/PVA/IPS)

The market is flooded with cheap ‘TN’ TFT panels. TN (twisted nematic) panels are terrible when it comes to reproducing color and have a very limited viewing angle. I used to have one and if I just slouched in my chair (or sat up too straight) the black level would change drastically. These panels are much cheaper to manufacture, so vendors have been flocking to them for years.

As artists, we need at least _decent_ color, even on our home machines. Because it can sometimes be difficult to determine the actual panel used in a diaplay, and because I care, I have compiled a list of > 24″ monitors and their panel type. I really would have liked to have seen this last week.

Product Panel Type Size HDMI Price
SAMSUNG 2433BW TN 24″
SAMSUNG T240HD TN 24″
SAMSUNG 2443BWX TN 24″
SAMSUNG 2443BW TN 24″
SAMSUNG 2493HM TN 24″
SAMSUNG 245BW TN 24″
SAMSUNG T260HD TN 26″
SAMSUNG 2693HM TN 26″
SAMSUNG 305T PVA 30″ NO 1100
SAMSUNG XL30 PVA / LED 30″ NO 3000
SAMSUNG SM2693HM TN 26″
NEC LCD2690WUXi IPS 26″ NO 1200
NEC LCD3090WQXi IPS 30″ NO 2200
NEC S2409W TN 24″
NEC 24WMGX3 TN 24″
DELL 2407WFP PVA 24″ YES
DELL 2408WFP PVA 24″ YES 517
DELL 3008WFP IPS 30″ YES 2000
DELL 3007WFP-HC IPS 30″ NO 1400
DELL 2709W PVA 27″ YES 900
DELL S2409W TN 24″
EIZO SX3031W PVA 30″ NO 3000
EIZO SX2761W PVA 27″ NO 2000
GATEWAY XHD3000 PVA 30″ YES 1000
HP W2408C TN 24″
HP W2558HC TN 26″
HP LP3065 IPS 30″ NO 1260
LG W2600H-PF TN 26″
LG W3000H-Bn IPS 30″ NO 1240
LG W2452T TN 24″
VIEWSONIC VP2650WB TN 26″
VIEWSONIC VA2626WM TN 26″
VIEWSONIC VX2835WM TN 28″
posted by admin at 7:25 am  

Saturday, November 29, 2008

Best Buy? I think not!

I really needed a stick of 800mhz DDR2. There’s a Best Buy somewhat close to here so I went over. When I get there, I see they have one stick of Kingston Value RAM, however it’s 145 DOLLARS. Thinking this was clearly a typo, I headed to the ‘Geek Squad’ guy at the register who scanned it and told me:

Nope, that’s how much this kind of RAM costs, it’s really a special kind‘. (yeah ‘value’)

I replied that it certainly was not. That is should be under 50 bucks ‘at any store’, he then laughed and told me that they match prices, but not ‘online only stores‘, to which I replied: ‘name a store, any store and that’s the price I will use‘. He said Fry’s (a popular brick and mortar store in Palo Alto) and we pulled up the website. The same RAM was 33 DOLLARS! Not on sale; nothing.

He called the manager, who came and said they couldn’t price match with a difference that large. I leveled with them… ‘Guys, look, it’s one stick of ‘value’ RAM. My PC is broken. I rode my bike here. Fry’s is in Palo Alto. I would pay double what it is at Frys, I am not tryin to rip you off, but I will not, on principle, bend over and take it like this; five times normal retail price is ridiculous!

The manager, seeing people behind me, started to talk down to me ‘We aren’t ripping you off, you are trying to price match to another store’s black friday ad! we only price match to real, non-sale prices!

I said ‘Look, it’s not a sale item, your own guy brought it up, name any store, where will you price match to?‘ He thought for a min ‘Central Computers, on Howard..‘ (they are not a chain and it would probably be more expensive..) ‘Ok, pull that up!’ We pulled the site up and the ram was 34 DOLLARS!

He turned to me quietly: ‘50 is as low as we can go.‘   ‘Sold!

I used to think Best Buy was decent, when you needed a component, if they had it, why go anywhere else? They are such a large chain that they can really discount items because they purchase in bulk. Like I said, this is what I used to think… While I have been in Germany the past 4 years apparently things have changed.

Have any of you seen anything this bad? Charging $145 for something all other retailers have for < $35 is just wrong. It irks me that they pay these ‘geeks’ in their ’squad’ to tell people lies from behind this knowledgeable facade.

posted by admin at 7:50 am  

Wednesday, September 17, 2008

Making of the Image Metrics ‘Emily’ Tech Demo

I have seen some of the other material in the SIGGRAPH Image Metrics presskit posted online [Emily Tech Demo] ['How To' video], but not the video that shows the making of the Emily tech demo. So here’s that as well:

[flv:http://www.ChrisEvans3D.com/video/image_metrics_making.flv 540 270]

At the end, there’s a quote from Peter Plantec about how Image Metrics has finally ‘crossed the uncanny valley’, but seriously, am I the only one who thinks the shading is a bit off, and besides that, what’s the point of laying a duplicate of face directly on top of one in a video? Shouldn’t they have shown her talking in a different setting? Maybe showed how they can remap the animation to a different face? There is no reason not to just use the original plate in this example.

posted by admin at 4:44 pm  

Saturday, June 21, 2008

Facial Stabilization in MotionBuilder using Python

Facial motion capture stabilization is basically where you isolate the movement of the face from the movement of the head. This sounds pretty simple, but it is actually a really difficult problem. In this post I will talk about the general process and give you an example facial stabilization python script.

Disclaimer: The script I have written here is loosely adapted from a MEL script in the book Mocap for Artists, and not something proprietary to Crytek. This is a great book for people of all experience levels, and has a chapter dedicated to facial mocap. Lastly, this script is not padded out or optimized.

To follow this you will need some facial mocap data, there is some freely downloadable here at www.mocap.lt. Grab the FBX file.

andy serkis - weta head stabilization halo

Stabilization markers

Get at least 3 markers on the actor that do not move when they move their face. These are called ’stabilization markers’ (STAB markers). You will use these markers to create a coordinate space for the head, so it is important that they not move. STAB markers are commonly found on the left and right temple, and nose bridge. Using a headband and creating virtual markers from multiple solid left/right markers works even better. Headbands move, it’s good to keep this in mind, above you see a special headrig used on Kong to create stable markers.

It is a good idea to write some tools to help you out here. At work I have written tools to parse a performance and tell me the most stable markers at any given time, if you have this data, you can also blend between them.

Load up the facial mocap file you have downloaded, it should look something like this:

In the data we have, you can delete the root, the headband markers, as well as 1-RTMPL, 1-LTMPL, and 1-MNOSE could all be considered STAB markers.

General Pipeline

As you can see, mocap data is just a bunch of translating points. So what we want to do is create a new coordinate system that has the motion of the head, and then use this to isolate the facial movement.

This will take some processing, and also an interactive user interface. You may have seen my tutorial on Creating Interactive MotionBuilder User Interface Tools. You should familiarize yourself with that because this will build on it. Below is the basic idea:

You create a library ‘myLib’ that you load into motionbuilder’s python environment. This is what does the heavy lifting, I say this because you don’t want to do things like send the position of every marker, every frame to your external app via telnet. I also load pyEuclid, a great vector library, because I didn’t feel like writing my own vector class. (MBuilder has no vector class)

Creating ‘myLib’

So we will now create our own library that sits inside MBuilder, this will essentially be a ‘toolkit’ that we communicate with from the outside. Your ‘myLib’ can be called anything, but this should be the place you store functions that do the real processing jobs, you will feed into to them from the outside UI later. The first thing you will need inside the MB python environment is something to cast FBVector3D types into pyEuclid. This is fairly simple:

#casts point3 strings to pyEuclid vectors
def vec3(point3):
	return Vector3(point3[0], point3[1], point3[2])
 
#casts a pyEuclid vector to FBVector3d
def fbv(point3):
	return FBVector3d(point3.x, point3.y, point3.z)

Next is something that will return an FBModelList of models from an array of names, this is important later when we want to feed in model lists from our external app:

#returns an array of models when given an array of model names
#useful with external apps/telnetlib ui
def modelsFromStrings(modelNames):
	output = []
	for name in modelNames:
		output.append(FBFindModelByName(name))
	return output

Now, if you were to take these snippets and save them as a file called myLib.py in your MBuilder directory tree (MotionBuilder75 Ext2\bin\x64\python\lib), you can load them into the MBuilder environment. (You should have also placed pyEuclid here)

casting fbvectors to pyeuclid

It’s always good to mock-up code in telnet because, unlike the python console in MBuilder, it supports copy/paste etc..

In the image above, I get the position of a model in MBuilder, it returns as a FBVector3D, I then import myLib and pyEuclid and use our function above to ‘cast’ the FBVector3d to a pyEuclid vector. It can now be added, subtracted, multiplied, and more; all things that are not possible with the default MBuilder python tools. Our other function ‘fbv()‘ casts pyEuclid vectors back to FBVector3d, so that MBuilder can read them.

So we can now do vector math in motionbuilder! Next we will add some code to our ‘myLib’ that stabilizes the face.

Adding Stabilization-Specific Code to ‘myLib’

One thing we will need to do a lot is generate ‘virtual markers’ from the existing markers. To do this, we need a function that returns the average position of however many vectors (marker positions) it is fed.

#returns average position of an FBModelList as FBVector3d
def avgPos(models):
	mLen = len(models)
	if mLen == 1:
		return models[0].Translation
	total = vec3(models[0].Translation)
	for i in range (1, mLen):
		total += vec3(models[i].Translation)
	avgTranslation = total/mLen
	return fbv(avgTranslation)

Here is an example of avgPos() in use:

Now onto the stabilization code:

#stabilizes face markers, input 4 FBModelList arrays, leaveOrig  for leaving original markers
def stab(right,left,center,markers,leaveOrig):
 
	pMatrix = FBMatrix()
	lSystem=FBSystem()
	lScene = lSystem.Scene
	newMarkers = []
 
	def faceOrient():
		lScene.Evaluate()
 
		Rpos = vec3(avgPos(right))
		Lpos = vec3(avgPos(left))
		Cpos = vec3(avgPos(center))
 
		#build the coordinate system of the head
		faceAttach.GetMatrix(pMatrix)
		xVec = (Cpos - Rpos)
		xVec = xVec.normalize()
		zVec = ((Cpos - vec3(faceAttach.Translation)).normalize()).cross(xVec)
		zVec = zVec.normalize()
		yVec = xVec.cross(zVec)
		yVec = yVec.normalize()
		facePos = (Rpos + Lpos)/2
 
		pMatrix[0] = xVec.x
		pMatrix[1] = xVec.y
		pMatrix[2] = xVec.z
 
		pMatrix[4] = yVec.x
		pMatrix[5] = yVec.y
		pMatrix[6] = yVec.z
 
		pMatrix[8] = zVec.x
		pMatrix[9] = zVec.y
		pMatrix[10] = zVec.z
 
		pMatrix[12] = facePos.x
		pMatrix[13] = facePos.y
		pMatrix[14] = facePos.z
 
		faceAttach.SetMatrix(pMatrix,FBModelTransformationMatrix.kModelTransformation,True)
		lScene.Evaluate()
 
	#keys the translation and rotation of an animNodeList
	def keyTransRot(animNodeList):
		for lNode in animNodeList:
			if (lNode.Name == 'Lcl Translation'):
				lNode.KeyCandidate()
			if (lNode.Name == 'Lcl Rotation'):
				lNode.KeyCandidate()
 
	Rpos = vec3(avgPos(right))
	Lpos = vec3(avgPos(left))
	Cpos = vec3(avgPos(center))
 
	#create a null that will visualize the head coordsys, then position and orient it
	faceAttach = FBModelNull("faceAttach")
	faceAttach.Show = True
	faceAttach.Translation = fbv((Rpos + Lpos)/2)
	faceOrient()
 
	#create new set of stabilized nulls, non-destructive, this should be tied to 'leaveOrig' later
	for obj in markers:
		new = FBModelNull(obj.Name + '_stab')
		newTran = vec3(obj.Translation)
		new.Translation = fbv(newTran)
		new.Show = True
		new.Size = 20
		new.Parent = faceAttach
		newMarkers.append(new)
 
	lPlayerControl = FBPlayerControl()
	lPlayerControl.GotoStart()
	FStart = int(lPlayerControl.ZoomWindowStart.GetFrame(True))
	FStop = int(lPlayerControl.ZoomWindowStop.GetFrame(True))
 
	animNodes = faceAttach.AnimationNode.Nodes
 
	for frame in range(FStart,FStop):
 
		#build proper head coordsys
		faceOrient()
 
		#update stabilized markers and key them
		for m in range (0,len(newMarkers)):
			markerAnimNodes = newMarkers[m].AnimationNode.Nodes
			newMarkers[m].SetVector(markers[m].Translation.Data)
			lScene.Evaluate()
			keyTransRot(markerAnimNodes)
 
		keyTransRot(animNodes)
 
		lPlayerControl.StepForward()

We feed our ‘stab function FBModelLists of right, left, and center stabilization markers, it creates virtual markers from these groups. Then ‘markers’ is all the markers to be stabilized. ‘leavrOrig’ is an option I usually add, this allows for non-destructive use, I have just made the fn leave original in this example, as I favor this, so this option does nothing, but you could add it. With the original markers left, you can immediately see if there was an error in your script. (new motion should match orig)

Creating an External UI that Uses ‘myLib’

Earlier I mentioned Creating Interactive MotionBuilder User Interface Tools, where I explain how to screenscrape/use the telnet Python Remote Server to create an interactive external UI that floats as a window in MotionBuilder itself. I also use the libraries mentioned in the above article.

The code for the facial stabilization UI I have created is here: [stab_ui.py]

I will now step through code snippets pertaining to our facial STAB tool:

def getSelection():
	selectedItems = []
	mbPipe("selectedModels = FBModelList()")
	mbPipe("FBGetSelectedModels(selectedModels,None,True)")
	for item in (mbPipe("for item in selectedModels: print item.Name")):
		selectedItems.append(item)
	return selectedItems

stab uiThis returns a list of strings that are the currently selected models in MBuilder. This is the main thing that our external UI does. The person needs to interactively choose the right, left, and center markers, then all the markers that will be stabilized.

At the left here you see what the UI looks like. To add some feedback to the buttons, you can make them change to reflect that the user has selected markers. We do so by changing the button text.

Example:

def rStabClick(self,event):
	self.rStabMarkers = getSelection()
	print str(self.rStabMarkers)
	self.rStab.Label = (str(len(self.rStabMarkers)) + " Right Markers")

This also stores all the markers the user has chosen into the variable ‘rStabMarkers‘. Once we have all the markers the user has chosen, we need to send them to ‘myLib‘ in MBuilder so that it can run our ‘stab‘ function on them. This will happen when they click ‘Stabilize Markerset‘.

def stabilizeClick(self,event):
	mbPipe('from euclid import *')
	mbPipe('from myLib import *')
	mbPipe('rStab = modelsFromStrings(' + str(self.rStabMarkers) + ')')
	mbPipe('lStab = modelsFromStrings(' + str(self.lStabMarkers) + ')')
	mbPipe('cStab = modelsFromStrings(' + str(self.cStabMarkers) + ')')
	mbPipe('markerset = modelsFromStrings(' + str(self.mSetMarkers) + ')')
	mbPipe('stab(rStab,lStab,cStab,markerset,False)')

Above we now use ‘modelsFromStrings‘ to feed ‘myLib’ the names of selected models. When you run this on thousands of frames, it will actually hang for up to a minute or two while it does all the processing. I discuss optimizations below. Here is a video of what you should have when stabilization is complete:


Kill the keyframes on the root (faceAttach) to remove head motion

Conclusion: Debugging/Optimization

Remember: Your stabilization will only be as good as your STAB markers. It really pays off to create tools to check marker stability.

Sometimes the terminal/screen scraping runs into issues. The mbPipe function can be padded out a lot and made more robust, this here was just an example. If you look at the external python console, you can see exactly what mbPipe is sending to MBuilder, and what it is receiving back through the terminal:

Sending&gt;&gt;&gt; selectedModels = FBModelList()
Sending&gt;&gt;&gt; FBGetSelectedModels(selectedModels,None,True)
Sending&gt;&gt;&gt; for item in selectedModels: print item.Name
['Subject 1-RH1', 'Subject 1-RTMPL']

All of the above can be padded out and optimized. For instance, you could try to do everything without a single lPlayerControl.StepForward() or lScene.Evaluate(), but this takes a lot of MotionBuilder/programming knowhow; it involves only using the keyframe data to generate your matrices, positions etc, and never querying a model.

posted by admin at 10:10 pm  

Friday, June 20, 2008

360 Degree Streaming Video

This is a video from a company called Immersive Media. It’s a 360 degree streaming video you can pan around, and even zoom in. Awesome stuff, their hardware even does realtime stitching, and they have an underwater housing. Check out the site for more vids, they have been to some great locations.

posted by admin at 12:01 pm  

Monday, June 16, 2008

High Speed Photography with the Casio EX-F1

At work we got the Casio EX-F1 for animation reference. It’s a really great, cheap solution for those looking to record high speed reference (300/600/1200fps) or hd (1080) video. Here are some videos I took a few weeks ago:

posted by admin at 5:27 pm  

Sunday, June 15, 2008

Building a J1 Remote Trigger for Vicon Datastations

Remote Trigger? Why Would I Want That?

Vicon Datastations allow you to string off a remote trigger which can allow you to start and stop of a motion capture take with a physical button. This could allow you to start/stop motion capture with sensors or anything else. In our case, we wanted to start/stop another device at the same exact time and have it sync’d with the mocap data, also, allow one person to run the device and the mocap session.

Disclaimer: I am aware that the remote interface is the same for the V8i/612/624/460/V6 Datastations, but I built this for the V8i, which looks like this:

This is what the ‘J1 REMOTE‘ port looks like on the back of your Datastation:

RTFM

Here is the description of the J1 Remote in the Vicon hardware manual:

Located directly below the camera inter face connectors, the J1 connector function is to allow the remote control of data capture from external switches or photoelectric sensors. Connecting Start (pin 3) or Stop (pin 5) to Ground (pin 7) will initiate the selected function.  Pin 1 generates a negative going TTL gated reference signal, which is aligned, to the camera Horizontal Synchronisation (HD) signal and present when data capture is being per formed.

The hardware manual will tell you that the J1 Remote Interface Connector is a Lemo Part (FGG.1B.307.CLAD52). So you will have to order this (follow the link). Below is the pin out from the manual, it’s pretty simple stuff:

Building the Trigger

Working with Relays

So, what we want to do is make a start and a stop button, or you could make an on/off switch. I made a button. The button flips a relay, which is like a switch. Below you see 5 pins, labeled ‘start‘, ‘stop‘, ‘grnd‘ and ‘coil‘. When you apply power to the coil, it will connect the grnd from stop to start and vice versa. Because it’s a magnet that flips the switch, nothing from the inner circuitry of the trigger can send any interference to the Vicon Datastation.

Below you see two relays, one triggers start/stop, the other triggers an LED. You can get relays that flip multiple poles at once. If you wanted to start/stop other devices with the same buttons you would add more relays, or use a multi-pole. In my example below I was sure to get relays and LEDs that work with a 9v battery, this way you do not need resistors or anything to alter the voltage.

The Altogether

This is what a final remote trigger can look like, green starts, red stops. The green LED can be on while capturing. The above relay will flip the light on/off based on button contact, even if red is pressed first, so you may want to go a different route if someone has butter fingers. The cord is durable microphone cord, as we only need 3 wires (start/stop/grnd, mic cable =  left/right/grnd).

Note: The J1 Remote Trigger works in Vicon Workstation, however, when Vicon updated it’s software to IQ, they did not want to spend the time to continue support of the remote trigger. IQ supports newer technology like the ‘MX Remote’ made by Vicon, which they would rather have you purchase. So yes, if you update your Vicon Software, certain features of your Vicon hardware will become useless.

posted by admin at 1:50 am  

Powered by WordPress