Skip to content

Commit

Permalink
Moved image editor class to its own file.
Browse files Browse the repository at this point in the history
TODO:
Improve setting max execution time code
Detect openCV proper
Add checkbox whether to use FD or not
  • Loading branch information
roborourke committed Mar 19, 2013
1 parent b7eed58 commit 6b25b04
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 115 deletions.
131 changes: 131 additions & 0 deletions editor/image-editor-gd-face-detect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php

/**
* Extend the GD image editor class as Face_detection relies on GD
*/
class WP_Image_Editor_GD_Detect_Face extends WP_Image_Editor_GD {

public $fd;
public $fd_file = 'detection.dat';
public $faces;

public function __construct( $file ) {
$this->file = $file;

// edit dims
add_filter( 'image_resize_dimensions', array( $this, 'face_crop' ), 10, 6 );

// memory usage is high
add_filter( 'image_memory_limit', array( $this, 'image_memory_limit' ), 10, 1 );
}


/**
* Alters the crop location of the GD image editor class by detecting faces
* and centering the crop around them
*
* @param array $output The parameters for imagecopyresampled()
* @param int $orig_w Original width
* @param int $orig_h Original Height
* @param int $dest_w Target width
* @param int $dest_h Target height
* @param bool $crop Whether to crop image or not
*
* @return array
*/
public function face_crop( $output, $orig_w, $orig_h, $dest_w, $dest_h, $crop ) {

// only need to detect if cropping
if ( $crop ) {

// detect face
if ( $this->faces === null ) {
// prepare face detector
$this->fd = new Face_Detector( FACE_DETECT_PATH . "php-facedetection/{$this->fd_file}" );

// detect face if we're cropping
$this->fd->face_detect( $this->image );
$this->faces = $this->fd->getFaces();

// save face data for other uses eg. tagging
if ( is_array( $this->faces ) && WP_Detect_Face::$attachment_id )
update_post_meta( WP_Detect_Face::$attachment_id, '_faces', $this->faces );
}

// if we have a face
if ( is_array( $this->faces ) ) {

// get faces area
$face_src_x = 9999999999999;
$face_src_y = 9999999999999;
$face_src_max_x = $face_src_max_w = 0;
$face_src_max_y = $face_src_max_h = 0;

// create bounding box
foreach( $this->faces as $face ) {
// left and top most x,y
if ( $face_src_x > $face[ 'x' ] ) $face_src_x = $face[ 'x' ];
if ( $face_src_y > $face[ 'y' ] ) $face_src_y = $face[ 'y' ];
// right and bottom most x,y
if ( $face_src_max_x < $face[ 'x' ] + $face[ 'w' ] ) $face_src_max_x = $face[ 'x' ] + $face[ 'w' ];
if ( $face_src_max_y < $face[ 'y' ] + $face[ 'w' ] ) $face_src_max_y = $face[ 'y' ] + $face[ 'w' ];
}

$face_src_w = $face_src_max_x - $face_src_x;
$face_src_h = $face_src_max_y - $face_src_y;

// crop the largest possible portion of the original image that we can size to $dest_w x $dest_h
$aspect_ratio = $orig_w / $orig_h;
$new_w = min($dest_w, $orig_w);
$new_h = min($dest_h, $orig_h);

if ( !$new_w ) {
$new_w = intval($new_h * $aspect_ratio);
}

if ( !$new_h ) {
$new_h = intval($new_w / $aspect_ratio);
}

$size_ratio = max($new_w / $orig_w, $new_h / $orig_h);

$crop_w = round($new_w / $size_ratio);
$crop_h = round($new_h / $size_ratio);

$src_x = floor( ($orig_w - $crop_w) / 2 );
$src_y = floor( ($orig_h - $crop_h) / 2 );

// bounding box
if ( $src_x == 0 ) {
$src_y = ( $face_src_y + ($face_src_h / 2) ) - ($crop_h / 2);
$src_y = min( max( 0, $src_y ), $orig_h - $crop_h );
}

if ( $src_y == 0 ) {
$src_x = ( $face_src_x + ($face_src_w / 2) ) - ($crop_w / 2);
$src_x = min( max( 0, $src_x ), $orig_w - $crop_w );
}

// the return array matches the parameters to imagecopyresampled()
// int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h
return array( 0, 0, $src_x, $src_y, $dest_w, $dest_h, $crop_w, $crop_h );
}

}

return null;
}

/**
* Increase the max timeout and double check memory limit
*
* @param string $limit The maximum memory limit allowed by PHP
*
* @return string
*/
public function image_memory_limit( $limit ) {
@set_time_limit( 60 );
return $limit;
}

}
143 changes: 28 additions & 115 deletions face-detect.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,17 @@
// Face detection
require_once 'php-facedetection/FaceDetector.php';

// Face detection image editor
require_once 'editor/image-editor-gd-face-detect.php';

// track attachment being modified
add_action( 'init', array( 'WP_Detect_Face', 'setup' ) );

class WP_Detect_Face {

/**
* @var int|null Reference to currently edited attachment post
*/
public static $attachment_id;

public function setup() {
Expand All @@ -36,127 +42,34 @@ public function setup() {

}

/**
* Hacky use of attached_file filters to get current attachment ID being resized
* Used to store face location and dimensions
*
* @param string $file File path
* @param int $attachment_id Attachment ID
*
* @return string The file path
*/
public function set_attachment_id( $file, $attachment_id ) {
self::$attachment_id = $attachment_id;
return $file;
}

/**
* Inserts face detect image editor prior to the standard GD editor
*
* @param array $editors Array of image editor class names
*
* @return array Image editor class names
*/
public function image_editors( $editors ) {
$editors = array_filter( $editors, function( $class ) {
return $class !== 'WP_Image_Editor_GD';
} );
array_push( $editors, 'WP_Image_Editor_GD_Detect_Face' );
return $editors;
}

}

/**
* Extend the GD image editor class as Face_detection relies on GD
*/
class WP_Image_Editor_GD_Detect_Face extends WP_Image_Editor_GD {

public $fd;
public $fd_file = 'detection.dat';
public $faces;


public function __construct( $file ) {
$this->file = $file;

// edit dims
add_filter( 'image_resize_dimensions', array( $this, 'face_crop' ), 10, 6 );

// memory usage is high
add_filter( 'image_memory_limit', array( $this, 'increase_memory_limit' ), 10, 1 );
}


public function face_crop( $output, $orig_w, $orig_h, $dest_w, $dest_h, $crop ) {
if ( $crop ) {

// detect face
if ( $this->faces === null ) {
// prepare face detector
$this->fd = new Face_Detector( FACE_DETECT_PATH . "php-facedetection/{$this->fd_file}" );

// detect face if we're cropping
$this->fd->face_detect( $this->image );
$this->faces = $this->fd->getFaces();

// save face data for other uses eg. tagging
if ( is_array( $this->faces ) && WP_Detect_Face::$attachment_id )
update_post_meta( WP_Detect_Face::$attachment_id, '_faces', $this->faces );
}

// if we have a face
if ( is_array( $this->faces ) ) {

// get faces area
$face_src_x = 9999999999999;
$face_src_y = 9999999999999;
$face_src_max_x = $face_src_max_w = 0;
$face_src_max_y = $face_src_max_h = 0;

// create bounding box
foreach( $this->faces as $face ) {
// left and top most x,y
if ( $face_src_x > $face[ 'x' ] ) $face_src_x = $face[ 'x' ];
if ( $face_src_y > $face[ 'y' ] ) $face_src_y = $face[ 'y' ];
// right and bottom most x,y
if ( $face_src_max_x < $face[ 'x' ] + $face[ 'w' ] ) $face_src_max_x = $face[ 'x' ] + $face[ 'w' ];
if ( $face_src_max_y < $face[ 'y' ] + $face[ 'w' ] ) $face_src_max_y = $face[ 'y' ] + $face[ 'w' ];
}

$face_src_w = $face_src_max_x - $face_src_x;
$face_src_h = $face_src_max_y - $face_src_y;

// crop the largest possible portion of the original image that we can size to $dest_w x $dest_h
$aspect_ratio = $orig_w / $orig_h;
$new_w = min($dest_w, $orig_w);
$new_h = min($dest_h, $orig_h);

if ( !$new_w ) {
$new_w = intval($new_h * $aspect_ratio);
}

if ( !$new_h ) {
$new_h = intval($new_w / $aspect_ratio);
}

$size_ratio = max($new_w / $orig_w, $new_h / $orig_h);

$crop_w = round($new_w / $size_ratio);
$crop_h = round($new_h / $size_ratio);

$src_x = floor( ($orig_w - $crop_w) / 2 );
$src_y = floor( ($orig_h - $crop_h) / 2 );

// bounding box
if ( $src_x == 0 ) {
$src_y = ( $face_src_y + ($face_src_h / 2) ) - ($crop_h / 2);
$src_y = min( max( 0, $src_y ), $orig_h - $crop_h );
}

if ( $src_y == 0 ) {
$src_x = ( $face_src_x + ($face_src_w / 2) ) - ($crop_w / 2);
$src_x = min( max( 0, $src_x ), $orig_w - $crop_w );
}

// the return array matches the parameters to imagecopyresampled()
// int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h
return array( 0, 0, $src_x, $src_y, $dest_w, $dest_h, $crop_w, $crop_h );
}

}

return null;
}


public function increase_memory_limit( $limit ) {
@set_time_limit(60);
return '128M';
$offset = array_search( 'WP_Image_Editor_GD', $editors );
return array_merge(
array_slice( $editors, 0, $offset ),
array( 'WP_Image_Editor_GD_Detect_Face' ),
array_slice( $editors, $offset, null )
);
}

}

0 comments on commit 6b25b04

Please sign in to comment.