David Serrano
Well-known member
Smooth color transitions. They are somewhat ugly but I don't care about it. The strange profile was caused by a quick Morphological :
Differences:
Full code:
Differences:
Code:
--- 3dplot/3dplot-0.7.js 2009-04-11 22:53:11.000000000 +0200
+++ 3dplot/3dplot-0.8.js 2009-04-12 00:32:50.000000000 +0200
@@ -16,6 +16,7 @@
params.polygonBorder=0x80000000;
params.useShading=true; // Apply shading effect
params.lightBrightness=2000; // Light intensity for the shading effect
+ params.palette_elems = 256;
params.palette = [ [0x80,0,0] , [0,0x80,0] , [0,0,0xC0], [0x80,0,0x80], [0x80,0x80,0], [0,0x80,0x80], [0x80,0x80,0x80] ];
return params;
@@ -108,6 +109,33 @@
return 0xFF000000 | (r<<16)| (g<<8) | b;
}
+// the resulting palette doesn't have to be exactly 'num' elements long.
+function ExpandPalette(params)
+{
+ var p = params.palette;
+ var num = params.palette_elems;
+ var expanded = new Array;
+ var steps = Math.round (num / p.length); // steps between two provided palette elements
+
+ for (var c = 0; c < p.length - 1; c++) {
+ var from = p[c];
+ var to = p[c+1];
+ var step_red = (to[0] - from[0]) / steps;
+ var step_green = (to[1] - from[1]) / steps;
+ var step_blue = (to[2] - from[2]) / steps;
+ for (s = 0; s < steps; s++) {
+ var new_red = from[0] + s * step_red;
+ var new_green = from[1] + s * step_green;
+ var new_blue = from[2] + s * step_blue;
+ expanded.push ([ new_red, new_green, new_blue ]);
+ }
+ }
+ // add last user-supplied palette element
+ expanded.push (p[p.length-1]);
+
+ params.palette = expanded;
+}
+
function RenderImage(src,i,limits,xform,perspecParams,minz)
{
// Start of the process
@@ -120,6 +148,8 @@
bmp.fill (perspecParams.backgroundColor);
+ ExpandPalette (perspecParams);
+
var fillbrush=[];
if(!perspecParams.useShading){
for(var c=0;c<perspecParams.palette.length;c++)
Full code:
Code:
#include <pjsr/UndoFlag.jsh>
#include <pjsr/FillRule.jsh>
#include <pjsr/ColorSpace.jsh>
// Creates the parameters object.
function DefaultParams()
{
var params={};
params.azimut = 30; // Perspective angle in degrees
params.elevation = 20; // Perspective elevation angle
params.scaleXY = 6; // Scale factor for X and Y axis
params.scaleZ = 50; // Scale factor for Z axis
params.mtf=0.5; // Midtones transfer value (0,1)
params.backgroundColor=0xff403000;
params.polygonFill=0xffffffff;
params.polygonBorder=0x80000000;
params.useShading=true; // Apply shading effect
params.lightBrightness=2000; // Light intensity for the shading effect
params.palette_elems = 256;
params.palette = [ [0x80,0,0] , [0,0x80,0] , [0,0,0xC0], [0x80,0,0x80], [0x80,0x80,0], [0,0x80,0x80], [0x80,0x80,0x80] ];
return params;
}
// Applies the perspective transformation to all the pixels in the image
function CreatePerspective(src, limits, perspecParams, minz)
{
src.initializeStatus( "Computing 3D coordinates", src.height );
// Calculates the coordinates of each pixel applying the perspective transformation
var xform=[];
limits.left=limits.top=1e10;
limits.right=limits.bottom=-1e10;
// Modify the zscale in order to normalize the peak height
// when the mtf changes
var zscale=perspecParams.scaleZ/(1-minz);
// Projection matrix
var alpha=Math.rad(90-perspecParams.elevation);
var beta=Math.rad(perspecParams.azimut);
var m00=Math.cos(beta);
var m10=-Math.sin(beta);
var m20=0;
var m01=Math.cos(alpha)*Math.sin(beta);
var m11=Math.cos(alpha)*Math.cos(beta);
var m21=-Math.sin(alpha);
for (var y = 0; y < src.height; y++) {
xform[y]=[];
for (var x = 0; x < src.width; x++){
var z= src.sample(x,y)*zscale;
var pixel=new Point( (m00*x+m10*y)*perspecParams.scaleXY,
(m01*x+m11*y+m21*z)*perspecParams.scaleXY );
xform[y][x]=pixel;
if(pixel.x<limits.left) limits.left=pixel.x;
if(pixel.x>limits.right) limits.right=pixel.x;
if(pixel.y<limits.top) limits.top=pixel.y;
if(pixel.y>limits.bottom) limits.bottom=pixel.y;
}
src.advanceStatus( 1 );
}
return xform;
}
function ApplyMTF(src, mtf)
{
// Faster solution using a process instance
var HT = new HistogramTransformation;
with ( HT )
{
H = // c0, m, c1, r0, r1
[[0, 0.5, 1, 0, 1],
[0, 0.5, 1, 0, 1],
[0, 0.5, 1, 0, 1],
[0, mtf, 1, 0, 1],
[0, 0.5, 1, 0, 1]];
}
var wtmp = new ImageWindow( 1, 1, 1, 16, false, src.colorSpace != ColorSpace_Gray );
var v = wtmp.mainView;
v.beginProcess( UndoFlag_NoSwapFile );
v.image.assign( src );
v.endProcess();
HT.executeOn( v, false ); // no swap file
src.assign( v.image );
wtmp.close();
}
function DieError(message)
{
var msgb=new MessageBox(message,"Error generating 3D view");
msgb.execute();
throw Error( message );
}
function ApplyLighting(basecolor, light)
{
var r=(basecolor[0]*light/1000);
r=r>255?255:r;
var g=(basecolor[1]*light/1000);
g=g>255?255:g;
var b=(basecolor[2]*light/1000);
b=b>255?255:b;
return 0xFF000000 | (r<<16)| (g<<8) | b;
}
// the resulting palette doesn't have to be exactly 'num' elements long.
function ExpandPalette(params)
{
var p = params.palette;
var num = params.palette_elems;
var expanded = new Array;
var steps = Math.round (num / p.length); // steps between two provided palette elements
for (var c = 0; c < p.length - 1; c++) {
var from = p[c];
var to = p[c+1];
var step_red = (to[0] - from[0]) / steps;
var step_green = (to[1] - from[1]) / steps;
var step_blue = (to[2] - from[2]) / steps;
for (s = 0; s < steps; s++) {
var new_red = from[0] + s * step_red;
var new_green = from[1] + s * step_green;
var new_blue = from[2] + s * step_blue;
expanded.push ([ new_red, new_green, new_blue ]);
}
}
// add last user-supplied palette element
expanded.push (p[p.length-1]);
params.palette = expanded;
}
function RenderImage(src,i,limits,xform,perspecParams,minz)
{
// Start of the process
var bmp = new Bitmap (i.width, i.height);
var g = new Graphics (bmp);
g.antialiasing=true;
// Applies a translation to the graphics to center the image
g.translateTransformation(-limits.left,-limits.top);
bmp.fill (perspecParams.backgroundColor);
ExpandPalette (perspecParams);
var fillbrush=[];
if(!perspecParams.useShading){
for(var c=0;c<perspecParams.palette.length;c++)
fillbrush[c]=new Brush(0xFF000000 | (perspecParams.palette[c][0]<<16) | (perspecParams.palette[c][1]<<8) | perspecParams.palette[c][2]);
g.pen = new Pen (perspecParams.polygonBorder,0);
} else
// Using shading the polygon borders should be less visible
g.pen = new Pen ((perspecParams.polygonBorder&0x00FFFFFF) | 0x20000000);
src.initializeStatus( "Rendering 3D profile", src.height-1 );
// Paint the polygons from back to front
for (var n = 0; n < src.height-1; n++) {
for (var m = 0; m < src.width-1; m++) {
var z1=src.sample(m,n),
z2=src.sample(m+1,n),
z3=src.sample(m,n+1),
z4=src.sample(m+1,n+1);
// Get the palette index
//var z=(z1+z2+z3+z4)/4;
var zM=Math.max(Math.max(z1,z2),Math.max(z3,z4));
var zm=Math.min(Math.min(z1,z2),Math.min(z3,z4));
var z=(zm+zM)/2;
z=(z-minz)/(1-minz); //rescale for mapping the palete to (minz,1)
var palidx=Math.round(z*perspecParams.palette.length);
palidx=palidx<0 ? 0 : (palidx>=perspecParams.palette.length ? perspecParams.palette.length-1 : palidx);
if(perspecParams.useShading){
// Get the lighting factor
var slope=z1-z2+z3-z4;
var lighting=(slope+0.5)*perspecParams.lightBrightness;
lighting=lighting<0 ? 0 : Math.round(lighting);
g.brush=new Brush(ApplyLighting(perspecParams.palette[palidx], lighting));
} else
g.brush=fillbrush[palidx];
var polygon=new Array(
xform[n][m],
xform[n][m+1],
xform[n+1][m+1],
xform[n+1][m]);
g.drawPolygon (polygon);
}
src.advanceStatus( 1 );
}
g.end();
i.blend (bmp);
}
function main()
{
if(ImageWindow.activeWindow.currentView.isNull)
return DieError("There is not an active image");
console.show();
console.writeln( "<end><cbr>
***** 3D Profiling Script *****" );
console.flush();
var startts = new Date;
// Extract the luminance of the source image
var src =new Image();
ImageWindow.activeWindow.currentView.image.extractLuminance(src);
// Inits the parameters
var perspecParams=DefaultParams();
if( perspecParams.mtf!=0.5)
ApplyMTF(src, perspecParams.mtf);
// Allow the user to abort this script
console.abortEnabled = true;
// Allow status monitoring for our working image
src.statusEnabled = true;
// The median of the source image is the level of the "floor" of the 3D image
var stats = new ImageStatistics;
stats.generate(src);
var minz=stats.median;
// Create the perspective
var limits=new Rect;
var xform=CreatePerspective(src,limits,perspecParams,minz);
//with(limits) console.writeln(format("%f %f %f %f",left,top,right,bottom));
// Create the output window
var newid;
if(ImageWindow.activeWindow.currentView.isPreview)
newid=ImageWindow.activeWindow.mainView.id+'_'+ImageWindow.activeWindow.currentView.id+'_3dplot';
else
newid=ImageWindow.activeWindow.currentView.id+'_3dplot';
var w = new ImageWindow (Math.round(limits.width), Math.round(limits.height), 3, 8, false, true, newid);
try{
var v = w.currentView;
var i = v.image;
v.beginProcess(UndoFlag_NoSwapFile);
RenderImage(src,i,limits,xform,perspecParams,minz);
v.endProcess();
w.show();
var endts = new Date;
console.writeln(format ("
3D view: %.2f s",(endts.getTime() - startts.getTime()) / 1000));
} catch(err)
{
w.close();
console.writeln(err);
}
}
main();