[PHP]複数の画像をランダムに敷き詰めて一枚の画像に合成

random

フォルダに入った大量の写真をランダムなタイル状に並べて一枚の画像にしてみました。

画像をタイル一枚の最低サイズを満たすようにリサイズし、座標を shuffle でランダムにしています。
全種類並べ終えたらもう一度シャッフルして並べているので、画像のバリエーションが少なくても
すべての領域を埋め尽くすことができます。ちなみに例では25種類使っています。

<?php
//画像全体のサイズ
$width = 600;
$height = 600;

//タイル一枚の最低サイズ
$block_size = 100;

//画像ディレクトリ
$dir = "image/";

$list = scandir($dir);
$images = array();

foreach($list as $value){
	if(!is_file ($dir . $value)) continue;
	$images[] = load_image( $dir . $value, $block_size );
}

$max_x = ceil($width / $block_size);
$max_y = ceil($height / $block_size);

shuffle($images);

$x_range = range(0, $max_x);
$y_range = range(0, $max_y);

shuffle($x_range);
shuffle($y_range);

$points = array();

foreach($x_range as $x){
	foreach($y_range as $y){
		$points[] = array("x" => $x, "y" => $y);
	}
}

$canvas = imagecreatetruecolor($width, $height);

foreach($points as $point){
	$x = $point['x'];
	$y = $point['y'];
		$current = current($images);
		imagecopy(
			$canvas, $current['resource'],
			$x * $block_size - ( round($current['width'] / 2) ),
			$y * $block_size - ( round($current['height'] / 2) ), 
			0, 0, $current['width'], $current['height']
		);
		$res = next($images);
		if($res === false) shuffle($images);
}


header("Content-type:image/jpeg");
imagejpeg($canvas, null, 80);
imagedestroy($canvas);

foreach($images as &$image){
	imagedestroy($image['resource']);
}

exit;

function load_image($filepath, $block_size){
	$checkimg = getimagesize($filepath);
	$width = $checkimg[0];
	$height = $checkimg[1];
 
	if($checkimg['mime'] == "image/jpeg" || $checkimg['mime'] == "image/pjpeg"){
		$extension = "jpg";
	} else if ($checkimg['mime'] == "image/gif"){
		$extension = "gif";
	} else if ($checkimg['mime'] == "image/png" || $checkimg['mime'] == "image/x-png"){
		$extension = "png";
	} else {
		exit;
	}
 
	if($extension == 'jpg'){$image = ImageCreateFromJPEG($filepath);}
	if($extension == 'gif'){$image = ImageCreateFromGIF($filepath);}
	if($extension == 'png'){$image = ImageCreateFromPNG($filepath);}
	 
	
	$scale = $block_size / min($width, $height);
	$thumb_width  = $width * $scale;
	$thumb_height = $height * $scale;
	
	$thumb = imagecreatetruecolor($thumb_width, $thumb_height);
	imagecopyresampled($thumb, $image, 0, 0, 0, 0, $thumb_width, $thumb_height, $width, $height);
	 
	$info = array();
	$info['resource']	 = $thumb;
	$info['width']		 = $thumb_width;
	$info['height']		 = $thumb_height;
	$info['extension']	 = $extension;
	
	imagedestroy($image);
	
	return $info;
}

写真素材 足成 http://www.ashinari.com/

[PHP]似た色合いの画像をRGB値をもとに探す

<?php
//比較元となる画像
$filepath = "sample.jpg";
 
//比較対象用画像ディレクトリ
$dir = "images/";
 
$sample = loadImage($filepath);
$sample_rgb = colorAverage($sample);
imagedestroy($sample);
 
$list = scandir($dir);
 
$files = array();
foreach($list as $value){
  if(is_file($dir . $value)){
    $files[] = $dir . $value;
  }
}
 
$diff = array();
foreach($files as $file){
  $image = loadImage($file);
  $rgb = colorAverage($image);
  $name = basename($file);
  $diff[$name] = colorDistance($sample_rgb, $rgb);
}
 
asort($diff);
$result = array_keys($diff);
echo reset($result);
 
function loadImage($filepath){
  $checkimg = getimagesize($filepath);
 
  if($checkimg['mime'] == "image/jpeg" || $checkimg['mime'] == "image/pjpeg"){
    $extension = "jpg";
  } else if ($checkimg['mime'] == "image/gif"){
    $extension = "gif";
  } else if ($checkimg['mime'] == "image/png" || $checkimg['mime'] == "image/x-png"){
    $extension = "png";
  } else {
    return false;
  }
 
  if($extension == 'jpg'){$image = ImageCreateFromJPEG($filepath);}
  if($extension == 'gif'){$image = ImageCreateFromGIF($filepath);}
  if($extension == 'png'){$image = ImageCreateFromPNG($filepath);}
 
   
  return $image;
}
 
function colorAverage($image){
  $width = imagesx($image);
  $height = imagesy($image);
 
  $thumb_width   = 16;
  $thumb_height   = 16;
  $thumb = imagecreatetruecolor($thumb_width, $thumb_height);
  imagecopyresampled($thumb, $image, 0, 0, 0, 0, $thumb_width, $thumb_height, $width, $height);
   
  $red   = 0;
  $green   = 0;
  $blue   = 0;
   
  for($x=0; $x < $thumb_width; $x++){
    for($y=0; $y < $thumb_height; $y++){
      $index   = imagecolorat($thumb, $x, $y);
      $rgb   = imagecolorsforindex($thumb, $index);
      $red   += $rgb['red'];
      $green   += $rgb['green'];
      $blue   += $rgb['blue'];
    }
  }
   
  $average = array();
  $pixel = $thumb_width * $thumb_height;
  $average['red']     = round($red / $pixel);
  $average['green']   = round($green / $pixel);
  $average['blue']   = round($blue / $pixel);
   
  return $average;
}
 
function colorDistance($rgb1, $rgb2){
  $distance = 0;
  $distance += abs($rgb1['red']   - $rgb2['red']);
  $distance += abs($rgb1['green']   - $rgb2['green']);
  $distance += abs($rgb1['blue']   - $rgb2['blue']);
  return $distance;
}

ある画像のRGB値から平均色を算出し、似た平均色を持つ画像をディレクトリ内から探すというものです。

計算速度を早めるために、一度小さなサムネイルを作ってから色を拾っています。精度を高めるならこのサムネイルサイズを大きくすると良いと思います。

ただ、結論から言うとイマイチです。

計算には間違いないのですが、人間の目は明るく鮮やかな部分に目が行ってしまい、暗く地味な色を無意識に省略してしまいます。
また、明るさの違いよりも色の違いを重視する傾向があります。

このプログラムの計算方法だと、色の違いと明るさの違いを同じ価値として計算するため、同じ色合いで明暗が異なる画像より、多少色が違っても似た明るさを持つ画像が重視されてしまいます。

そのあたりを考慮するのであればHSV色空間(色相・彩度・明度)をもとに比較すべきなのかもしれません。

追記: HSV色空間を利用した画像検索も作りました。
また、Lab 色空間を利用した精度の高い類似画像検索についてはこちらの記事を御覧ください。

[PHP]画像を連結して数値を表示する

アクセスカウンター等でよく見る、数字の描かれた画像を連結して一枚の画像として表示する場合のソースです。

<?php
//カウント
$count = 12345;
//桁
$figure = 8;

//数字画像のサイズ
$width	 = 16;
$height	 = 24;

$count = sprintf("%0{$figure}d", $count);

$length = strlen($count);

$base = ImageCreateTrueColor($width * $length, $height);

for($i=0;$i<$length;$i++){
	$num = substr($count, $i, 1);
	$digit = ImageCreateFromJpeg("img/{$num}.jpg");
	ImageCopy($base, $digit, $i * $width, 0, 0, 0, $width, $height);
	imagedestroy($digit);
}

header("Content-Type: image/jpeg");
imagejpeg($base, NULL, 80);
imagedestroy($base);

counter

今回は「1.jpg」のように数字がファイル名になっている統一サイズの画像を使いました。
先頭をゼロで埋める必要がない場合は sprintf の部分を消して下さい。

画像とソース