Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

albedo, illumination and normal renderer added #98

Merged
merged 18 commits into from
Apr 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package scalismo.faces.parameters
import scalismo.faces.color.RGBA
import scalismo.faces.image.PixelImage
import scalismo.faces.mesh.{ColorNormalMesh3D, VertexColorMesh3D}
import scalismo.faces.render.{TriangleFilters, TriangleRenderer, ZBuffer}
import scalismo.faces.render.{PixelShader, TriangleFilters, TriangleRenderer, ZBuffer}
import scalismo.mesh.{MeshSurfaceProperty, TriangleMesh3D}

import scala.reflect.ClassTag
Expand Down Expand Up @@ -50,6 +50,33 @@ object ParametricRenderer {
buffer).toImage
}

/**
* render a mesh with specified colors and normals according to scene description parameter
*
* @param parameter scene description
* @param mesh mesh to render, has positions, colors and normals
* @param pixelShader used to calculate the color for a pixel
* @param clearColor background color of buffer
* @return
*/
def renderParameterMesh[A: ClassTag](parameter: RenderParameter,
mesh: ColorNormalMesh3D,
pixelShader: PixelShader[A],
clearColor: A): PixelImage[A] = {
val buffer = ZBuffer(parameter.imageSize.width, parameter.imageSize.height, clearColor)

val worldMesh = mesh.transform(parameter.modelViewTransform)
val backfaceCullingFilter = TriangleFilters.backfaceCullingFilter(worldMesh.shape, parameter.view.eyePosition)

TriangleRenderer.renderMesh[A](
mesh.shape,
backfaceCullingFilter,
parameter.pointShader,
parameter.imageSize.screenTransform,
pixelShader,
buffer).toImage
}

/**
* render according to parameters, convenience for vertex color mesh with vertex normals
*
Expand All @@ -64,6 +91,22 @@ object ParametricRenderer {
renderParameterMesh(parameter, ColorNormalMesh3D(mesh), clearColor)
}

/**
* render according to parameters, convenience for vertex color mesh with vertex normals
*
* @param parameter scene description
* @param mesh mesh to render, vertex color, vertex normals
* @param pixelShader used to calculate the color for a pixel
* @param clearColor background color of buffer
* @return
*/
def renderParameterVertexColorMesh[A:ClassTag](parameter: RenderParameter,
mesh: VertexColorMesh3D,
pixelShader: PixelShader[A],
clearColor: A ): PixelImage[A] = {
renderParameterMesh(parameter, ColorNormalMesh3D(mesh), pixelShader, clearColor)
}

/**
* render the object described by the render parameters
*
Expand All @@ -77,6 +120,21 @@ object ParametricRenderer {
renderParameterMesh(parameter, mesh, clearColor)
}

/**
* render the object described by the render parameters
*
* @param parameter scene description, including the object
* @param pixelShader used to calculate the color for a pixel
* @param clearColor background color of scene/buffer
* @return
*/
def renderParameter[A: ClassTag](parameter: RenderParameter,
pixelShader: PixelShader[A],
clearColor: A): PixelImage[A] = {
val mesh = RenderObject.instance(parameter.momo)
renderParameterMesh(parameter, mesh, pixelShader, clearColor)
}

/**
* render an arbitrary property on the mesh into buffer (rasterization)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,38 +74,6 @@ abstract class RenderFromCorrespondenceImage[A](correspondenceMoMoRenderer: Corr
override def renderImage(parameters: RenderParameter): PixelImage[A]
}

case class DepthMapRenderer(correspondenceMoMoRenderer: CorrespondenceMoMoRenderer, clearColor: RGBA = RGBA.Black) extends RenderFromCorrespondenceImage[RGBA](correspondenceMoMoRenderer: CorrespondenceMoMoRenderer) {
override def renderImage(parameters: RenderParameter): PixelImage[RGBA] = {
val correspondenceImage = correspondenceMoMoRenderer.renderCorrespondenceImage(parameters)
val depthMap = correspondenceImage.map{ px=>
if(px.isDefined){
val frag = px.get
val tId = frag.triangleId
val bcc = frag.worldBCC
val mesh = frag.mesh

val posModel = mesh.position(tId, bcc)
val posEyeCoordinates = parameters.modelViewTransform(posModel)

Some((parameters.view.eyePosition-posEyeCoordinates).norm)
}else{
None
}
}
val values = depthMap.values.toIndexedSeq.flatten
val ma = values.max
val mi = values.min
val mami = ma-mi
depthMap.map{d=>
if(d.isEmpty)
clearColor
else {
RGBA(1.0 - (d.get - mi)/mami)
}
}
}
}

case class CorrespondenceColorImageRenderer(correspondenceMoMoRenderer: CorrespondenceMoMoRenderer, backgroundColor: RGBA = RGBA.Black) extends RenderFromCorrespondenceImage[RGBA](correspondenceMoMoRenderer){
val reference: VertexColorMesh3D = correspondenceMoMoRenderer.model.mean
val normalizedReference: SurfacePointProperty[RGBA] = {
Expand Down
23 changes: 2 additions & 21 deletions src/main/scala/scalismo/faces/sampling/face/MoMoRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,30 +29,11 @@ import scalismo.utils.Memoize

/** parametric renderer for a Morphable Model, implements all useful Parameteric*Renderer interfaces */
class MoMoRenderer(val model: MoMo, val clearColor: RGBA)
extends ParametricImageRenderer[RGBA]
extends ParametricModel(model)
with ParametricLandmarksRenderer
with ParametricMaskRenderer
with ParametricMeshRenderer
with ParametricModel {

/** pad a coefficient vector if it is too short, basis with single vector */
private def padCoefficients(coefficients: DenseVector[Double], rank: Int): DenseVector[Double] = {
require(coefficients.length <= rank, "too many coefficients for model")
if (coefficients.length == rank)
coefficients
else
DenseVector(coefficients.toArray ++ Array.fill(rank - coefficients.length)(0.0))
}

/** create an instance of the model, in the original model's object coordinates */
override def instance(parameters: RenderParameter): VertexColorMesh3D = {
instanceFromCoefficients(parameters.momo)
}

/** draw a model instance directly from the coefficients */
def instanceFromCoefficients(instance: MoMoInstance): VertexColorMesh3D = {
model.instance(instance.coefficients)
}
with ParametricImageRenderer[RGBA] {

/** render the image described by the parameters */
override def renderImage(parameters: RenderParameter): PixelImage[RGBA] = {
Expand Down
120 changes: 120 additions & 0 deletions src/main/scala/scalismo/faces/sampling/face/ModalityRenderers.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright University of Basel, Graphics and Vision Research Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package scalismo.faces.sampling.face

import scalismo.faces.color.{RGB, RGBA}
import scalismo.faces.image.PixelImage
import scalismo.faces.parameters.{ColorTransform, ParametricRenderer, RenderParameter}
import scalismo.geometry.{Point, Vector, _3D}
import scalismo.mesh.{BarycentricCoordinates, SurfacePointProperty, TriangleId}


object ModalityRenderers {

object DepthMapRenderer {
def apply(correspondenceMoMoRenderer: CorrespondenceMoMoRenderer) = new DepthMapRenderer(correspondenceMoMoRenderer)
}
class DepthMapRenderer(correspondenceMoMoRenderer: CorrespondenceMoMoRenderer) extends ParametricImageRenderer[Option[Double]] {

override def renderImage(parameters: RenderParameter): PixelImage[Option[Double]] = {
val correspondenceImage = correspondenceMoMoRenderer.renderCorrespondenceImage(parameters)
correspondenceImage.map { optFrag =>
optFrag.map { fragment =>
parameters.renderTransform(fragment.mesh.position(fragment.triangleId, fragment.worldBCC)).z
}
}
}

}

object NormalsRenderer {
def apply(correspondenceMoMoRenderer: CorrespondenceMoMoRenderer) = new NormalsRenderer(correspondenceMoMoRenderer)
}
class NormalsRenderer(correspondenceMoMoRenderer: CorrespondenceMoMoRenderer) extends ParametricImageRenderer[Option[Vector[_3D]]] {

override def renderImage(parameters: RenderParameter): PixelImage[Option[Vector[_3D]]] = {
val correspondenceImage = correspondenceMoMoRenderer.renderCorrespondenceImage(parameters)
correspondenceImage.map { optFrag =>
optFrag.map { fragment =>
parameters.modelViewTransform(fragment.mesh.vertexNormals(fragment.triangleId, fragment.worldBCC))
}
}
}

def renderNormalsVisualization(parameters: RenderParameter, clearColor: RGBA = RGBA.BlackTransparent): PixelImage[RGBA] = {
val normals = renderImage(parameters)
colorNormalImage(normals, clearColor)
}
}

object AlbedoRenderer{
def apply(correspondenceMoMoRenderer: CorrespondenceMoMoRenderer, clearColor: RGBA = RGBA.BlackTransparent) = new AlbedoRenderer(correspondenceMoMoRenderer, clearColor)
}
class AlbedoRenderer(correspondenceMoMoRenderer: CorrespondenceMoMoRenderer, clearColor: RGBA) extends ParametricImageRenderer[RGBA] {

override def renderImage(parameters: RenderParameter): PixelImage[RGBA] = {
val instance = correspondenceMoMoRenderer.instance(parameters)
val correspondenceImage = correspondenceMoMoRenderer.renderCorrespondenceImage(parameters)
correspondenceImage.map { optFrag =>
optFrag.map { fragment =>
instance.color.onSurface(fragment.triangleId, fragment.worldBCC)
}.getOrElse(clearColor)
}
}
}

object IlluminationVisualizationRenderer {
def apply(correspondenceMoMoRenderer: CorrespondenceMoMoRenderer, clearColor: RGBA = RGBA.BlackTransparent) = new IlluminationVisualizationRenderer(correspondenceMoMoRenderer, clearColor)
}
class IlluminationVisualizationRenderer(correspondenceMoMoRenderer: CorrespondenceMoMoRenderer, clearColor: RGBA) extends ParametricImageRenderer[RGBA] {

override def renderImage(parameters: RenderParameter) : PixelImage[RGBA] = {
val instance = correspondenceMoMoRenderer.instance(parameters)
val noColorInst = instance.copy(color = SurfacePointProperty(instance.shape.triangulation, instance.color.pointData.map(_ => RGBA(0.5, 0.5, 0.5))))
val correspondenceImage = correspondenceMoMoRenderer.renderCorrespondenceImage(parameters)
val shader = parameters.noColorTransform.pixelShader(noColorInst)
correspondenceImage.map{ optFrag =>
optFrag.map{ fragment =>
shader(fragment.triangleId,fragment.worldBCC,Point(fragment.x,fragment.y,fragment.z))
}.getOrElse(clearColor)
}
}

}

def normalizeDepthMapIamge(depthMap: PixelImage[Option[Double]], clearValue: Double) : PixelImage[Double] = {
val values = depthMap.values.flatten.toIndexedSeq
if (!values.isEmpty) {
val max = values.max
val min = values.min
val mami = max-min
depthMap.map(v => v.map(d => (d-min)/mami).getOrElse(clearValue))
} else {
depthMap.map(v => v.getOrElse(clearValue))
}
}

def colorNormalImage(normals: PixelImage[Option[Vector[_3D]]], clearColor: RGBA) : PixelImage[RGBA] = {
normals.map { opt =>
opt.map { normal =>
val v = normal * 0.5
RGBA(0.5 - v.x, 0.5 - v.y, 0.5 - v.z, 1.0)
}.getOrElse(clearColor)
}
}

}
25 changes: 22 additions & 3 deletions src/main/scala/scalismo/faces/sampling/face/ParametricModel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,29 @@

package scalismo.faces.sampling.face

import breeze.linalg.DenseVector
import scalismo.faces.mesh.VertexColorMesh3D
import scalismo.faces.parameters.RenderParameter
import scalismo.faces.momo.MoMo
import scalismo.faces.parameters.{MoMoInstance, RenderParameter}

/** generates a model instance in original model coordinates */
trait ParametricModel {
def instance(parameters: RenderParameter): VertexColorMesh3D
class ParametricModel(model: MoMo ) {
/** pad a coefficient vector if it is too short, basis with single vector */
private def padCoefficients(coefficients: DenseVector[Double], rank: Int): DenseVector[Double] = {
require(coefficients.length <= rank, "too many coefficients for model")
if (coefficients.length == rank)
coefficients
else
DenseVector(coefficients.toArray ++ Array.fill(rank - coefficients.length)(0.0))
}

/** create an instance of the model, in the original model's object coordinates */
def instance(parameters: RenderParameter): VertexColorMesh3D = {
instanceFromCoefficients(parameters.momo)
}

/** draw a model instance directly from the coefficients */
def instanceFromCoefficients(instance: MoMoInstance): VertexColorMesh3D = {
model.instance(instance.coefficients)
}
}