Solved

PHP Convert Image To 16 Level Grayscale, Then Read Each Pixel

Posted on 2011-02-11
11
811 Views
Last Modified: 2012-05-11
I have a client in need of a solution... preferable in PHP as I am not that skilled in ASP.net - yet.

Client is an artist mainly working in acrylic and works in a cross between expressionism and abstract. One of his experimental efforts requires an application to be built so that he can break down tonal information.

Basically, he needs a web based application that will allow him to upload/import a color image or black and white image, have it converted to 16 level gray scale and then identify each pixel value to display/print in line order:

Example Output:
1 3 5 2 0    C D F 2 4   4 3 5 6 4   3 4 6 7 3   1 3 4 5 6
2 3 4 5 5    3  6 8 5 6   3 4 5 6 1   2 4 6 8 9   1 2 4 5 8
1 3 5 2 0    C D F 2 4   4 3 5 6 4   3 4 6 7 3   1 3 4 5 6
2 3 4 5 5    3  6 8 5 6   3 4 5 6 1   2 4 6 8 9   1 2 4 5 8
2 3 4 5 5    3  6 8 5 6   3 4 5 6 1   2 4 6 8 9   1 2 4 5 8

Where EACH NUMBER/LETTER would identify the gray level of each pixel, as follows:

1= Level 1 Gray
2= Level 2 Gray
3= Level 3 Gray
4= Level 4 Gray
5= Level 5 Gray
6= Level 6 Gray
7= Level 7 Gray
8= Level 8 Gray
9= Level 9 Gray
0= Level 10 Gray
A= Level 11 Gray
B= Level 12 Gray
C= Level 13 Gray
D= Level 14 Gray
E= Level 15 Gray
F= Level 16 Gray

In the above example, Image would be 25 pixels wide, 20 pixels tall  - but the function needs to accommodate images of varying widths and heights.

What I need help with is a PHP5 Function to convert the image to 16 Level/Bit Grayscale, and then a separate function to read the pixel of each image.  I do know the GD Library is compiled into PHP, but I do not believe ImageMagik is.  I checked using PhpInfo() and it isn't in the list.

From the hours of reading I have done, I am pretty sure it is possible - I am just not sure how exactly.

Any help someone can provide with be very much appreciated!

Thanks!
0
Comment
Question by:nyxano
  • 5
  • 5
11 Comments
 
LVL 14

Expert Comment

by:Scott Madeira
Comment Utility
Try this.  I think it does what you want.  I have echo statements in there to show color values. You could save to an array or do whatever you want with the data.

 
<?php 

	// Read your image file
	$im = imagecreatefromjpeg('5docs.jpg');
	
	// Convert to 16 level Grayscale
	imagetograyscale($im);
	
	// Get color values
	imagegetcolors($im);
	
	
	// Save new image
	imagejpeg($im, '5docs16.jpg');
	
	
	
	
	function imagetograyscale($im)
		{
			// Convert to Grayscale
			imagefilter($im, IMG_FILTER_GRAYSCALE);
			
			// Convert grayscale image to pallette image
		    if (imageistruecolor($im)) {		    
		        imagetruecolortopalette($im, false, 256);
		    }
		
		    for ($c = 0; $c < imagecolorstotal($im); $c++) {
		        $col = imagecolorsforindex($im, $c);
		        
		        // Get value for red (which should be the same for blue and green becuase it is grayscale
		        // Convert from 256 level to 16 level by using the higer 4 bits (00 - 0F woudl be 00, 80-8F would be 88, etc.)
		        $gray = $col['red'] >> 4 & 0xF;
        		$gray = $gray*16;        
        		imagecolorset($im, $c, $gray, $gray, $gray);
		        		        
		        imagecolorset($im, $c, $gray, $gray, $gray);
		    }
		}
	
	
	function imagegetcolors($im){
			
			$imgw = imagesx($im);
			$imgh = imagesy($im);
			
			echo 'height = '.$imgh;
			
			// If image is not truecolr then convert to truecolor
			if (! imageistruecolor($im)) {		    
		        $imgTC = imagecreatetruecolor($imgw, $imgh);
				imagecopy($imgTC, $im, 0, 0, 0, 0, $imgw, $imgh);
				$im = $imgTC;
		    }
	
			for ($x = 0 ; $x < $imgw; $x++){
				for ($y = 0 ; $y < $imgh; $y++){	
					// Get pixel color				
					$rgb = imagecolorat($im, $x, $y);
					//get top 4 bits - upper half of red color
					$r = ($rgb >> 20) & 0xFF;
					echo 'Values: R='.$r ."\n";
					
				
				}
			}		
	}
	

	imagedestroy($im);	
	
?>

Open in new window

0
 
LVL 33

Expert Comment

by:Slick812
Comment Utility
greetings nyxano,  Below is my php code to get the output you have listed above, but I have a - instead of many spaces
1 3 5 2 0 - C D F 2 4 - 4 3 5 6 4 - 3 4 6 7 3 - 1 3 4 5 6

but I have no idea why you are using groups of 5? ?

also I use a more correct "HEX" as you have your ZERO above the 9 instead of where it should be , below the 1

Ask questions if you need more info or corrections. .
<html><head><title>Greyscale PNG test</title></head><body bgcolor="#E3F7FF"><h2>GD color PNG to greyscale Image test</h2>
<?php
$img = imagecreatefrompng('images3/color.png');
imagefilter($img, IMG_FILTER_GRAYSCALE);
// if your version of GD does not have imagefilter( ), there are other ways
$hex1 = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
$pixelOut = array();
$wid = imagesx($img);
$hgt = imagesy($img);
for($x = 0; $x < $wid; ++$x){$pixelOut[x] = array();
	for($y = 0; $y < $hgt; ++$y){//if($x==1)echo $y;
		//$pixelOut[$x][$y]= $y;
		$index = imagecolorat($img, $x, $y);
		//$rgb = imagecolorsforindex($img,$index);
		$index &= 255;
		$pixelOut[$x][$y]= $hex1[floor($index/16)];//$index;//$rgb['red'];//; $rgb['red']
		//$color = imagecolorallocate($im, 255 - $rgb['red'], 255 - $rgb['green'], 255 - $rgb['blue']);
		//imagesetpixel($img, $x, $y, $color);
		}
	}
// you can change the output formatting below
for($y = 0; $y < $hgt; ++$y){
	for($x = 0; $x < $wid; ++$x){
		echo $pixelOut[$x][$y].' ';
		if ($x%5 == 4) echo '- ';
		}
	echo '<br />';
	}
?>
<p>More Text here</p>
</body></html>

Open in new window

0
 

Author Comment

by:nyxano
Comment Utility
@smadeira
Thank you for the solution.  I have not had a chance to try it yet but will do so this weekend.

@Slick812
Your solution seems similar to that of smadeira but I do like that you took the time to also add the output code in as well.  As for why groups of 5?  I honestly do not have an answer except to say that I think it simply allows the output to be easier to read.  I will double check with the client to see if there is any other reason but I suspect that will be the reasoning.  I will try your solution this weekend as well and see which one best produces the results required.

Thank you to the both of you. Excellent work and I greatly appreciate the quick response.
0
 

Author Comment

by:nyxano
Comment Utility
@smadeira
Sorry, I didn't see before I posted my last comment that you also echoed the output.  I will be testing both solutions soon and we'll take it from there.
0
 
LVL 33

Expert Comment

by:Slick812
Comment Utility
I was in a hurry when I did it before, I looked at my code and could see that I do not need to loop through the pixels twice, and can do the operation in a single loop, which should be much more efficient, and fast, with alot less code to maintain.
try this out -
<html><head><title>Greyscale PNG test</title></head><body bgcolor="#E3F7FF"><h2>GD color PNG to greyscale Image test</h2>
<?php
$img = imagecreatefrompng('images3/color.png');
imagefilter($img, IMG_FILTER_GRAYSCALE);
$hex1 = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
	
for($y = 0; $y < imagesy($img); ++$y){
	for($x = 0; $x < imagesx($img); ++$x){
		$color = imagecolorat($img, $x, $y);
		$color &= 255;
		echo $hex1[floor($color/16)].' ';
		if ($x%5 == 4) echo '- ';
		}
	echo '<br />';
	}

imagedestroy($img);

?>
<p>More Text here</p>
</body></html>

Open in new window

0
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 

Author Comment

by:nyxano
Comment Utility
@Slick812
The new code you've provided is definitely optimized.  I think that should run a lot faster.

I just got word from the client, and the scope of the project has changed slightly.  I think I should be able to take your code and amend it - but just to list the changes.

1.  If image is larger than 200x200, it needs to be resized down to 200x200 (while maintaining aspect ratio).

2.  The resized image from # 1 needs to be saved on the server so that it can be downloaded.

I already know how to do both of those tasks already so I am assuming that after I resize the image, I just load the image name/location into your $img= variable?

I still haven't tested the code yet but from what I see, it should work.  If you want to add code in for the extra options, that would be great - but if not, that's okay too.  Once I test the code, I'll be able to let you know how it all works out.
0
 
LVL 33

Expert Comment

by:Slick812
Comment Utility
It is late here now, I may can do something tomorrow, please run my code to see if your PHP has problems with it, it works on mine.
0
 
LVL 33

Accepted Solution

by:
Slick812 earned 500 total points
Comment Utility
I have reworked this php app to the new design specs you have given, however, I had to change the "200x200 (while maintaining aspect ratio" down to "100x100", I did this in order to have the display of an image pixel ROW in the image not to wrap into 2 lines on the screen in the output display, But you can change the $dimention variable to whatever max small size you decide to use. I also had to not use the "spaces" between the HEX letters and numbers, for the same reason, to reduce the horizontal size of a display row.
<html><head><title>Greyscale PNG test</title></head><body bgcolor="#E3F7FF">
<h2>GD color PNG to greyscale Image test</h2>
<?php
$img = imagecreatefrompng('images3/bigcolor.png');

$hex1 = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');

// the $dimention below sets the smaller resized image MAX dimention
$dimention = 100; // 200
	// I tried this with the dimention as 200, BUT I have a real wide monitor, and 
	//the "rows" of display ran more than the width of my screen, so changed to 100

$wid = imagesx($img);
$hgt = imagesy($img);
echo  'old width- '.$wid.' old height- '.$hgt.'<br />';

if(($wid>$dimention)|($hgt>$dimention)){
	if ($wid>$hgt) {$wid2=$dimention;
		$hgt2=ceil(($hgt/$wid)*$dimention);}
		elseif ($hgt>$wid) {$hgt2=$dimention;
		$wid2=ceil(($wid/$hgt)*$dimention);}
		else {$hgt2=$dimention; $wid2=$dimention;}
	if ($hgt2 < 3) $hgt2 = 3;
	if ($wid2 < 3) $wid2 = 3;
	$resize = imagecreatetruecolor($wid2, $hgt2);
	// I had to use the less quality resize of imagecopyresized() instaed of imagecopyresampled().
	imagecopyresized($resize, $img, 0, 0, 0, 0, $wid2, $hgt2, $wid, $hgt);
	//the imagecopyresampled() really alterd the colors to crap values for a 16 color greyscale image
	imagedestroy($img);
	$img = $resize;
	$hgt = $hgt2;
	$wid = $wid2;
	echo  'new width- '.$wid.' new height- '.$hgt.'<br />';
	imagepng($img, 'images3/resize1.png');
	// the save file above needs your file path and file name
	}


imagefilter($img, IMG_FILTER_GRAYSCALE);
for($y = 0; $y < $hgt; ++$y){
	for($x = 0; $x < $wid; ++$x){
		$color = imagecolorat($img, $x, $y);
		$color &= 255;
// I had to eliminate the extra spaces inorder to get each pixel row NOT to wrap around on my screen
		echo $hex1[floor($color/16)];//.' '
		if ($x%5 == 4) echo ' ';
		}
	echo '<br />';
	}
imagedestroy($img);

?>
<p><img src="images3/resize1.png"></p>
</body></html>

Open in new window

0
 

Author Comment

by:nyxano
Comment Utility
@Slick812
Just want to say I haven't forgotten about this. Right now, client is away and difficult to reach. From what I see, everything looks great and I should be able to take it from there and fill in the rest of the requirements. I just want to make sure there are no other major changes needed. I shoul be able to clue up this on the weekend.
0
 
LVL 33

Expert Comment

by:Slick812
Comment Utility
OK, there's not much I could do anyway, if you are hired by your client, you should do any additional work, since I don't get paid.
0
 

Author Closing Comment

by:nyxano
Comment Utility
Solution was spot on for what I needed.  I was then able to take the code and adapt it to meet specific needs.
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Introduction HTML checkboxes provide the perfect way for a web developer to receive client input when the client's options might be none, one or many.  But the PHP code for processing the checkboxes can be confusing at first.  What if a checkbox is…
This article discusses how to create an extensible mechanism for linked drop downs.
The viewer will learn how to dynamically set the form action using jQuery.
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

6 Experts available now in Live!

Get 1:1 Help Now