Page 1 of 1

3D Landscape Effect

Posted: 2010-07-28T20:16:42-07:00
by DJ Mike
Turn an image into a 3D landscape. Each pixel is turned into a line with it's height proportional to it's grayscale value.

The ImagickPixelIterator class Example #2: 3D Landscape
http://eclecticdjs.com/mike/tutorials/p ... dscape.php

Re: 3D Landscape Effect

Posted: 2010-07-28T21:14:22-07:00
by fmw42
Nice to be able to do something like that in IM.

For reference, see my publications on my web site at http://www.fmwconcepts.com/fmw/fmw.html

and particular my survey paper F.M. Weinhaus and V. Devarajan, Texture Mapping 3D Models of Real-World Scenes, ACM Computing Surveys, Vol. 29, No. 4, 325-365, December 1997.

Re: 3D Landscape Effect

Posted: 2010-07-29T19:19:59-07:00
by el_supremo
Mike,
I think the reason that this script uses so much ram and takes a long time is because you don't need to use pixel iterators at all because you aren't modifying the source image like you do in the ripple example. In fact, you don't actually use the iterators other than to find which row you are currently working on and there's a much easier way to do that by using a for loop.

Pete

Re: 3D Landscape Effect

Posted: 2010-08-02T13:29:13-07:00
by DJ Mike
el_supremo wrote:Mike,
I think the reason that this script uses so much ram and takes a long time is because you don't need to use pixel iterators at all because you aren't modifying the source image like you do in the ripple example. In fact, you don't actually use the iterators other than to find which row you are currently working on and there's a much easier way to do that by using a for loop.

Pete
Thanks. As obvious as that is now I missed it until you pointed it out. I tried running the avatar example again today and it ran for 8 minutes then gave me a 500 error. I changed it to the nested while loops that are posted below and it "only" took 1:26 the first time and 0:28 the second run. Now that I see that it isn't a good example of pixel iteration I'll have to think of somewhere else to put it.

Code: Select all

<?php
//set php script timeout, 0 to disable
set_time_limit(0);

# starting image
$url = "http://eclecticdjs.com/mike/images/avatar.jpg";

$file = "3d_avatar.jpg"; # name of finished image

# Read image as blob
$blob = file_get_contents("$url");
# make new imagick object from blob
$image = new imagick();
$image->readImageBlob("$blob");

# scale it down
$image->scaleimage(150, 0);
$image->shearImage("transparent", 45,0);

# scale it to make it look like it is laying down
$w = $image->getimagewidth();
$h = $image->getimageheight();
$image->scaleimage($w, $h/2);

# Get image stats
$w = $image->getimagewidth();
$h = $image->getimageheight();
$format = $image->getimageformat(); 

#### Make a pallet to draw on
$pallete = new Imagick;
$pallete->newimage($w,$h*2, "blue");
$p_w = $image->getimagewidth();
$p_h = $image->getimageheight();
$dif = $p_h-$h;

$pallete->setimageformat("$format");
####

$row = new ImagickPixelIterator($image);
$x = 1;
$y = 1;
#############################
# loop through all points
# while ( $row->getNextIteratorRow() )
while ( $y <= $h )
#############################
{
while ( $x <= $w)
{
# get (r,g,b) and grey value
$point = $image->getImagePixelColor( $x, $y );
$color = $point->getColor();
$r = $color[r];
$g = $color[g];
$b = $color[b];

# Calculate grayscale
$grey = ($r+$g+$b)/25;
$grey =(int)$grey;

# Make line with height proportional to grayscale 
$line = new ImagickDraw;
$line->setfillcolor("rgb($r,$g,$b)");
$line->setstrokecolor("rgb($r,$g,$b)");
#$line->setstrokewidth(1);
$line_height = $y-$grey;
$offset = 75;

#############################
#$y = $row->getIteratorRow();
#$y++;
#############################

$start_y = $y+$offset;
$end_y = $y-$grey+$offset;
$line->line( $x, $start_y, $x, $end_y);

# draw the line
$pallete->drawimage($line);

# Free up memory
$point->destroy();
$line->destroy();
unset($color);
$x++;
}
$x = 1;
$y++;
}

$pallete->scaleimage(250, 150);
# write pallet
$pallete->writeimage( "$file");

#header("content-type:image/$format");
header("Location:$file");echo $pallete;
exit;

?>

Re: 3D Landscape Effect

Posted: 2010-08-02T14:04:57-07:00
by fmw42
In general, I think for loops are faster than while loops. At least that has been my experience on my Mac when scripting in bash.

Re: 3D Landscape Effect

Posted: 2010-08-02T15:29:23-07:00
by el_supremo
Hi Mike,
A few comments on your new code:
1. You still instantiate an ImagickPixelIterator but don't use it - probably forgot to comment it out

Code: Select all

	$row = new ImagickPixelIterator($image);
2. The while loops would be easier to read as for loops and when indexing
into an image with ImageMagick the indices start at zero so either use

Code: Select all

	$x = 0;
	$y = 0;
	while ( $y < $h )
		{
		while ( $x < $w)

OR use

Code: Select all

	for($y=0;$y<$h;$y++)
		{
		for($x=0;$x<$w;$x++)
and remove $x++; and $x=1;$y++; from the bottom of the loops.

3. I converted your example to C and I'm using it as a MagickWand Example in C (see my sig).
While doing this I found that it is about 25% faster to create, draw and destroy
the "line" drawingwand outside the inner loop. Your code is similar to this:

Code: Select all

	for($y=0;$y<$h;$y++)
		{
		for($x=0;$x<$w;$x++)
		{
			.
			.
			.
			$line = new ImagickDraw;
			.
			.
			$line->line( $x, $start_y, $x, $end_y);
			
			# draw the line
			$pallete->drawimage($line);
			$line->destroy();
		
		}

It will execute faster arranged like this:

Code: Select all

	for($y=0;$y<$h;$y++)
		{
		$line = new ImagickDraw;
		for($x=0;$x<$w;$x++)
		{
			.
			.
			$line->line( $x, $start_y, $x, $end_y);
			
		}	
		# draw the line
		$pallete->drawimage($line);
		$line->destroy();
I also found that drawing the image from top to bottom, while it is easy to do, requires drawing a lot of lines which will only be overwritten by a later line that is in "front" of them and so covers them up. I modified my C version so that it starts at the bottom of the image and goes up each column, keeping track of the length of the most recently drawn line and only drawing a new one if it is longer. This can speed up the process by a factor of four or more. If you're interested, have a look at my code at http://members.shaw.ca/el.supremo/Magic ... ape_3d.htm

Pete

Re: 3D Landscape Effect

Posted: 2010-08-02T16:14:38-07:00
by DJ Mike
Thanks for the tips. I don't know C but I can follow almost all of it and I copied to study later.

Re: 3D Landscape Effect

Posted: 2010-08-02T16:37:12-07:00
by DJ Mike
I have not modified the scripts yet but I thought this other example was nice. I knew that my multi-color shirt wouldn't make a good 3D effect but the illusion of my hand standing out of the rest of the image is pretty convening. I changed the $gray divisor to 100 to make the hand less exaggerated and more lifelike.

Before
http://eclecticdjs.com/mike/tutorials/p ... possum.jpg

After
http://eclecticdjs.com/mike/tutorials/p ... possum.jpg