기준점에 따른 이미지 크롭(이미지의 특정 부분만 복사) 처리하기2 > 개발강좌

개발강좌

프로그램 강좌 :
1. 유창화님의 썸네일, 정규표현식, 이미지관련 강좌
2. Sphinx 검색엔진을 이용한 도로명 주소 검색 시스템 구축

기준점에 따른 이미지 크롭(이미지의 특정 부분만 복사) 처리하기2 정보

썸네일 기준점에 따른 이미지 크롭(이미지의 특정 부분만 복사) 처리하기2

본문

대단할것은 없는 강좌이지만,
제 강좌를 출처를 밝히고 외부로 퍼가는 것은 허용하지만,
다른 강좌의 자료나 책의 자료로 사용되거나 부분적인 인용은 허용하지 않습니다.

강좌는 php 5. 대를 기준으로 하며, 기본적으로 GD 나 FREETYPE 등의 기본적인 라이브러리는 연동되었다는 가정하에 진행합니다.
예전 개발 환경에서는 GD 나 FREETYPE 등의 연동여부나 php버젼들을 따졌지만, 요새 개발 환경에서는 대부분 기본적으로 다 제공하기 때문에 그렇습니다.

기준점에 따른 이미지 크롭(이미지의 특정 부분만 복사) 처리하기2

이전에는 이미지 크롭 처리하는 것의 일부분을 다루어 보았습니다.
이번에는 이번에는 원본과 썸네일의 크기만 가지고 자동으로 크롭하고 리사이즈처리하는 부분을 다루어 보도록 하겠습니다.

문제1>  너비가 1280이고 높이가 960인 test.jpg라는 원본 이미지가 있습니다.이 이미지로 다음 세개의 썸네일 이미지를 만드세요.
128 X 96 ==> test_crop_128X96.jpg
128 X 86 ==> test_crop_128X86.jpg
128 X 106 ==> test_crop_128X106.jpg
단 정비율로서 만들되 남는 부분이나 큰부분을 중앙을 기준으로 크롭하여 생성하세요.

처리힌트>
문제가 좀 어렵게 느껴질수 있습니다.
첫번째 썸네일은 10분의 1로 축소한 이미지 입니다.
두번째 이미지는 10분의 1로 축소하되 높이가 96보다 작으므로 높이 중앙을 기준으로 크롭을 해야 합니다.
세번째 이미지는 10분의 1로 축소해서 보면 높이가 96보다 크기 때문에 복사해오면 여백이 남으므로 다시 높이를 기준으로 비율을 계산하여 너비 중앙을 기준으로 크롭을 해야 합니다.

처리과정>
1. 먼저 원본이미지로 부터 get_image_resource_from_file 함수를 통해 이미지 리소스를 가져온다.

2. 최종 생성될 이미지(썸네일이미지)의 리소스를 imagecreatetruecolor 함수를 통해 세개 (128 X 96 , 128 X 86, 128 X 106) 생성합니다.

3. 각 썸네일의 너비를 기준으로 크롭할지 여부와 크롭 기준점, 크롭영역을 계산합니다.

4. 원본이미지로 부터 각 기준점과 크롭영역으로 크롭 및 리사이즈를 합니다.

5. 처리된 썸네일 이미지의 리소스들을 각 test_crop_128X96.jpg, test_crop_128X86.jpg, test_crop_128X106.jpg test_crop.jpg 로 저장합니다.

다른건 크게 문제 될것이 없다고 보이고 3번이 좀 문제가 되리라고 봅니다.
편의상 128 X 96 을 1번, 128 X 86 을 2번, 128 X 106 을 3번 이미지라고 부르겠습니다.
일단, 세개의 이미지가 모두 너비가 128 이므로 너비를 기준으로 계산을 하겠습니다.

원본 1280 X 960 의 이미지를 128 의 정비율 썸네일로 만든다고 생각합니다. 그러면 자동으로 썸네일의 높이가 결정됩니다.
그 높이(96) 로 만들어야할 세개의 썸네일의 높이와 비교합니다.

1번은 동일하므로 크롭없이 리사이즈하면 됩니다.

2번은 96보다 작으므로 높이를 기준으로 크롭후 리사이즈 해야합니다.
따라서 원본에서 크롭을 해야할 기준점과 영역을 구해야 합니다.
너비는 비율대로 이고 높이가 문제이므로 크롭할 영역은 1280  X 860 입니다.
이유는 0.1 비율로 너비가 축소되기 때문에 높이는 반대의 경우로 86 * 10 이기 때문입니다.
너비는 동일하고 높이가 다르므로, 중앙을 기준으로 크롭한다고 했으니까......
크롭할 기준점은 0, 50 입니다.
이유는 이전 내용에서도 설명했지만 왼쪽 꼭지점에서 높이만 아래로 내려오는 것이기 때문에 X좌표는 0 이고 Y좌표는 (960 - 860 ) / 2 입니다. 물론 소숫점이 나올수 있으므로 ceil을 사용합니다.

3번은 96보다 크므로 정상적으로 복사를 해오게 되면 썸네일의 높이부분에 남는 영역 생깁니다.
그러므로 기존 가로 기준에서 높이 기준으로 바꿔야 합니다.
기존에는 너비기준이었기 때문에 비율이 0.1 이엇지만 높이 기준으로 바꾸게 되면 0.114..... 가 됩니다.
이것을 기준으로 썸네일의 너비를 새로 구하게 되면 142 가 됩니다. 물론 소숫점은 올림으로 처리하였습니다.
높이는 비율대로 이고 너비가 문제이므로 크롭할 영역은 1160 X 960 입니다.
이유는 약 0.11XX 비율로 너비가 축소되기 때문에 높이는 반대의 경우로 128 * 9.05XX 이기 때문입니다.
높이는 동일하고 너비가 다르므로, 중앙을 기준으로 크롭한다고 했으니까......
크롭할 기준점은 60, 0 입니다.
이유는 이전 내용에서도 설명했지만 왼쪽 꼭지점에서 너비만 오른쪽으로 이동하는것이기 때문에 Y좌표는 0 이고 X좌표는 (1280 - 1160 ) / 2 입니다. 물론 소숫점이 나올수 있으므로 ceil을 사용합니다.


테스트를 하기 이전에 기존 함수 수정과 새로운 함수 두개를 추가해야 합니다.

아래 get_size_by_rule 을 조금 수정해야 합니다. 이전에는 예제에서 소숫점이 나올경우가 없었기 때문에 리턴값을 계산후 그냥 반환하였습니다. 하지만, 정확하게는 소숫점도 나올수 있으므로 ceil 함수를 사용하여 리턴값을 반환해야 합니다. 따라서 기존 함수를 아래와 같이 수정합니다.

예제2 : lib/image_proc.function.php 의 get_size_by_rule 수정
//원본의 너비, 원본의 높이, 리사이즈 너비나 높이, 기준값을 받아 기준값을 토대로 정비율의 값을 구함
//성공시 정비율의 값을 반환, 실패시 false를 반환
//기준값은 width 나 height, 기준값은 생략 가능하며 생략시 자동으로 width가 된다.
function get_size_by_rule($src_w, $src_h, $dst_size, $rule='width'){

  if (!is_int($src_w) || $src_w < 1 || !is_int($src_h) || $src_h < 1){//원본의 너비와 높이가 둘중에 하나라도 0보다 큰 정수가 아닐경우

    $GLOBALS['errormsg'] = "원본의 너비와 높이가 0보다 큰 정수가 아닙니다. ($src_w, $src_h)";
    return false;
  }

  if (!is_int($dst_size) || $dst_size < 1){//리사이즈 될 사이즈가 0보다 큰 정수가 아닐경우

    $GLOBALS['errormsg'] = "리사이즈될 사이즈가 0보다 큰 정수가 아닙니다. ($dst_size)";
    return false;
  }

  if ($rule != 'height') {//기준값이 너비일 경우

    return ceil($dst_size / $src_w * $src_h);
  }
  else {//기준값이 높이일 경우

    return ceil($dst_size / $src_h * $src_w);
  }
}


예제2 : lib/image_proc.function.php 에 추가
//원본의 너비나 높이, 썸네일 너비, 썸네일 높이, 기준값을 받아 기준값을 토대로 정비율의 값을 구함
//성공시 정비율의 값을 반환, 실패시 false를 반환
//기준값은 width 나 height, 기준값은 생략 가능하며 생략시 자동으로 width가 된다.
function get_bigsize_by_rule($src_size, $dst_w, $dst_h, $rule='width'){

  if (!is_int($src_size) || $src_size < 1){//원본의 사이즈가 0보다 큰 정수가 아닐경우

    $GLOBALS['errormsg'] = "원본의 사이즈가 0보다 큰 정수가 아닙니다. ($src_size)";
    return false;
  }

  if (!is_int($dst_w) || $dst_w < 1 || !is_int($dst_h) || $dst_h < 1){//썸네일의 너비와 높이가 둘중에 하나라도 0보다 큰 정수가 아닐경우

    $GLOBALS['errormsg'] = "썸네일의 너비와 높이가 0보다 큰 정수가 아닙니다. ($dst_w, $dst_h)";
    return false;
  }

  if ($rule != 'height') {//기준값이 너비일 경우

    return ceil($src_size / $dst_w * $dst_h);
  }
  else {//기준값이 높이일 경우

    return ceil($src_size / $dst_h * $dst_w);
  }
}

lib/image_proc.function.php 파일에 위 함수 get_bigsize_by_rule 를 추가 합니다.
기존 get_size_by_rule 는 큰 너비와 큰 높이를 알때 정비율의 작은 값을 구하는 것이었다면 get_bigsize_by_rule 은 작은 너비와 작은 높이를 알때 정비율의 큰 값을 구하는 것입니다.

get_bigsize_by_rule($src_size, $dst_w, $dst_h, $rule='width')
용도 : 썸네일 크기에 대해 원본 이미지의 정비율 크기를 구할때 사용, 성공시 0보다 큰 정수를 반환, 실패시 false를 반환합니다.

인자 :
$src_size ==> 원본의 너비나 높이, 0보다 큰 정수를 사용합니다
$dst_w ==> 썸네일의 너비, 0보다 큰 정수를 사용합니다.
$dst_h ==> 썸네일의 높비, 0보다 큰 정수를 사용합니다.
$rule ==> 너비를 기준으로 값을 생성할지, 높이를 기준으로 값을 생성할지 결정하는 구분자. 너비를 기준으로 할때는 width, 높이를 기준으로 할때는 height, $rule이 생략되거나, $rule 이 height 가 아닌경우에는 모두 width 로 인식합니다.


예제3 : lib/image_proc.function.php 에 추가
//원본의 리소스, 원본의 너비, 원본의 높이, 크롭리사이즈 너비, 높이를 받아 이미지 크롭 후 리사이즈 처리
//성공시 리사이즈된 이미지의 리소스를 반환, 실패시 false를 반환
function get_image_cropresize($src, $src_w, $src_h, $dst_w, $dst_h=0){

  if (empty($src))  {//원본의 리소스가 빈값일 경우

    $GLOBALS['errormsg'] = '원본 리소스가 없습니다.';
    return false;
  }

  //정수형이 아니라면 정수형으로 강제 형변환
  if (!is_int($src_w)) settype($src_w, 'int');
  if (!is_int($src_h)) settype($src_h, 'int');
  if (!is_int($dst_w)) settype($dst_w, 'int');
  if (!is_int($dst_h)) settype($dst_h, 'int');

  if ($src_w < 1 || $src_h < 1){//원본의 너비와 높이가 둘중에 하나라도 0보다 큰 정수가 아닐경우

    $GLOBALS['errormsg'] = "원본의 너비와 높이가 0보다 큰 정수가 아닙니다. ($src_w, $src_h)";
    return false;
  }

  if (empty($dst_w) && empty($dst_h)) {//리사이즈될 너비와 높이 둘다 없을 경우

    $GLOBALS['errormsg'] = '리사이즈될 너비와 높이는 둘중에 하나는 반듯이 있어야 합니다.';
    return false;
  }

  if (!empty($dst_w) && $dst_w < 1){//리사이즈 될 너비가 존재하는데 0보다 큰 정수가 아닐경우

    $GLOBALS['errormsg'] = "리사이즈될 너비가 0보다 큰 정수가 아닙니다. ($dst_w)";
    return false;
  }

  if (!empty($dst_h) && $dst_h < 1){//리사이즈 될 높이가 존재하는데 0보다 큰 정수가 아닐경우

    $GLOBALS['errormsg'] = "리사이즈될 높이가 0보다 큰 정수가 아닙니다. ($dst_h)";
    return false;
  }

  //리사이즈 될 너비와 높이가 둘중에 하나가 없는 경우에는 정비율을 의미하며, 비율데로 너비와 높이를 결정한다.
  if (empty($dst_w) || empty($dst_h)) {

    if (empty($dst_h)) $dst_h = get_size_by_rule($src_w, $src_h, $dst_w, 'width');
    else $dst_w = get_size_by_rule($src_w, $src_h, $dst_h, 'height');
  }

  //만들어질 $dst_w , $dst_h 크기의 이미지 리소스를 생성한다.
  $dst = @imagecreatetruecolor ($dst_w , $dst_h);
  if ($dst === false) {

    $GLOBALS['errormsg'] = "$dst_w , $dst_h 크기의 썸네일 이미지의 리소스를 생성하지 못했습니다.";
    return false;
  }

  //먼저 리사이즈 너비를 기준으로 정비율 리사이즈 높이를 계산한다.
  $s_w = $dst_w;
  $s_h = get_size_by_rule($src_w, $src_h, $s_w, 'width');

  if ($dst_h == $s_h) {//높이가 같을 경우, 즉 정비율 리사이즈일경우

    $result_resize = imagecopyresampled ($dst , $src , 0 , 0 , 0 , 0 , $dst_w , $dst_h , $src_w , $src_h );
    if ($result_resize === false) {

      $GLOBALS['errormsg'] = "$dst_w , $dst_h 크기로 리사이즈에 실패하였습니다.";
      return false;
    }
  }
  else if ($dst_h < $s_h) {//지정된 높이가 정비율 높이 보다 작을경우, 높이를 기준으로 가운데를 크롭

    //썸네일의 높이를 기준으로 정비율의 원본 높이를 구한다.
    $src_nh = get_bigsize_by_rule($src_w, $dst_w, $dst_h, 'width');

    $src_x = 0;
    $src_y = ceil(($src_h - $src_nh) / 2);

    $result_resize = imagecopyresampled ($dst , $src , 0 , 0 , $src_x , $src_y , $dst_w , $dst_h , $src_w , $src_nh );
    if ($result_resize === false) {

      $GLOBALS['errormsg'] = "$dst_w , $dst_h 크기로 리사이즈에 실패하였습니다.";
      return false;
    }
  }
  else {//지정된 높이가 정비율 높이 보다 큰경우, 너비를 기준으로 가운데를 크롭

    //썸네일의 너비를 기준으로 정비율의 원본 너비를 구한다.
    $src_nw = get_bigsize_by_rule($src_h, $dst_w, $dst_h, 'height');

    $src_x = ceil(($src_w - $src_nw) / 2);
    $src_y = 0;

    $result_resize = imagecopyresampled ($dst , $src , 0 , 0 , $src_x , $src_y , $dst_w , $dst_h , $src_nw , $src_h );
    if ($result_resize === false) {

      $GLOBALS['errormsg'] = "$dst_w , $dst_h 크기로 리사이즈에 실패하였습니다.";
      return false;
    }
  }

  return $dst;
}

lib/image_proc.function.php 파일에 위 함수 get_image_cropresize 를 추가 합니다.
기존 get_image_resize 은 단순 이미지 리사이즈만을 위한 함수라면get_image_cropresize 어느 한쪽 부분이 비율보다 클 경우, 비율대로 계산하여 중앙을 기준으로 크롭후 리사이즈 해주는 함수입니다.

get_image_resize($src, $src_w, $src_h, $dst_w, $dst_h=0)
용도 : 원본 이미지의 리소스를 받아 새로운 크기의 썸네일 이미지를 생성하여 그 이미지의 리소스를 반환합니다. 성공시에는 이미지 리소스를 반환하고, 실패시에는 false를 반환합니다. get_image_resize 와 다른 점은 중앙을 기준으로 크롭후 리사이즈 합니다.

인자 :
$src ==> 원본의 이미지 리소스
$src_w ==> 원본의 너비, 0보다 큰 정수를 사용합니다.
$src_h ==> 원본의 높이, 0보다 큰 정수를 사용합니다.
$dst_w ==> 새로 만들 이미지의 너비를 지정합니다. 0이상의 정수를 사용합니다. 0을 사용할때는 $dst_h 에는 반듯이 0보다 큰 정수를 사용해야 합니다.
$dst_h ==> 새로 만들 이미지의 높이를 지정합니다. 0이상의 정수를 사용합니다. 0을 사용할때는 $dst_w 에는 반듯이 0보다 큰 정수를 사용해야 합니다.

처리순서 :
1. 원본 리소스의 값이 있는지 체크
2. 정수형으로 사용되어져야할 변수들이 정수가 아니면 정수로 강제 변환
3. 원본의 너비와 높이가 0보다 큰 정수인지 체크
4. 리사이즈 이미지의 너비 높이 둘다 값이 없는지 체크
5. 리사이즈 이미지의 너비나 높이에 값이 있을때 0보다 큰 정수인지 체크
6. 리사이즈 이미지의 너비나 높이, 둘중 하나가 없을때 정비율로 나머지 값을 구하는 부분
7. 리사이즈 이미지 리소스 생성
8. 이미지 크롭 및 리사이즈 처리
9. 생성된 이미지 리소스 반환


아래의 예제를 통해 위 문제1을 처리해 가는 과정을 확인합니다.
예제4 : test9.php
<?php

//이미지 처리 함수 인클루드
include_once 'lib/image_proc.function.php';

$path_file = 'sample_image/test.jpg';//원본파일

$path_crop_128X96_file = 'sample_image/test_crop_128X96.jpg';//크롭 및 리사이즈 처리되어 저장될 파일 경로
$path_crop_128X86_file = 'sample_image/test_crop_128X86.jpg';//크롭 및 리사이즈 처리되어 저장될 파일 경로
$path_crop_128X106_file = 'sample_image/test_crop_128X106.jpg';//크롭 및 리사이즈 처리되어 저장될 파일 경로

//원본의 이미지 리소스를 받아온다.
list($src, $src_w, $src_h) = get_image_resource_from_file ($path_file);
if (empty($src)) die($GLOBALS['errormsg'] . "<br />\n");

//128 X 96 처리
$dst_w = 128;
$dst_h = 96;
$dst_128X96 = get_image_cropresize($src, $src_w, $src_h, $dst_w, $dst_h);
if ($dst_128X96 === false) die($GLOBALS['errormsg'] . "<br />\n");

$result_save_128X96 = save_image_from_resource ($dst_128X96,
$path_crop_128X96_file);//저장
if ($result_save_128X96 === false) die($GLOBALS['errormsg'] . "<br />\n");

@imagedestroy($dst_128X96);

//128 X 86 처리
$dst_w = 128;
$dst_h = 86;
$dst_128X86 = get_image_cropresize($src, $src_w, $src_h, $dst_w, $dst_h);
if ($dst_128X86 === false) die($GLOBALS['errormsg'] . "<br />\n");

$result_save_128X86 = save_image_from_resource ($dst_128X86, $path_crop_128X86_file);//저장
if ($result_save_128X86 === false) die($GLOBALS['errormsg'] . "<br />\n");

@imagedestroy($dst_128X86);

//128 X 106 처리
$dst_w = 128;
$dst_h = 106;
$dst_128X106 = get_image_cropresize($src, $src_w, $src_h, $dst_w, $dst_h);
if ($dst_128X106 === false) die($GLOBALS['errormsg'] . "<br />\n");

$result_save_128X106 = save_image_from_resource ($dst_128X106, $path_crop_128X106_file);//저장
if ($result_save_128X106 === false) die($GLOBALS['errormsg'] . "<br />\n");

@imagedestroy($dst_128X106);


@imagedestroy($src);

//성공하였다면 이미지 출력

?>
원본 이미지 <br />
<img src='<?=$path_file?>'> <br />

썸네일 이미지 - 크롭 및 리사이즈 128px X 96px <br />
<img src='<?=$path_crop_128X96_file?>'> <br />

썸네일 이미지 - 크롭 및 리사이즈 128px X 86px <br />
<img src='<?=$path_crop_128X86_file?>'> <br />

썸네일 이미지 - 크롭 및 리사이즈 128px X 106px <br />
<img src='<?=$path_crop_128X106_file?>'> <br />


오늘 예제에서는 크롭을 너비 기준으로 하였지만, 실전에서는 높이 기준으로 처리해야 할때와 여러 기준점을 줄때도 있습니다.
하지만 그러한 것들은 다 응용이 가능한 부분들이고 강좌 특성상 이정도로 크롭은 마무리 짓도록 하겠습니다.
다음 시간에는 워터마크에 대해 다뤄볼까 합니다.
추천
3

댓글 3개

전체 17
개발강좌 내용 검색 썸네일에서

회원로그인

(주)에스아이알소프트 / 대표:홍석명 / (06211) 서울특별시 강남구 역삼동 707-34 한신인터밸리24 서관 1404호 / E-Mail: admin@sir.kr
사업자등록번호: 217-81-36347 / 통신판매업신고번호:2014-서울강남-02098호 / 개인정보보호책임자:김민섭(minsup@sir.kr)
© SIRSOFT