Skip to content

Instantly share code, notes, and snippets.

@masazdream
Created August 19, 2014 11:03
Show Gist options
  • Save masazdream/d1cf1a091b46d5aea2f9 to your computer and use it in GitHub Desktop.
Save masazdream/d1cf1a091b46d5aea2f9 to your computer and use it in GitHub Desktop.
数学計算をする昔つくったphpクラス
<?php
/**
* 幾何学計算の汎用クラス
*
* @author kiban
*/
class pjGeometry
{
/**
* 質問のユーザ選択領域を訓練画像上の点へのアフィン変換を実施する
*
* @param $question_id ユーザ選択領域を取得する質問ID
* @param $grids アフィアン行列用座標点(480×640との対応点)
*
* @return 座標点が足りない場合falseを返す。
*/
public static function getAffineTransform($question_id, $grids){
// 配列のチェック
if(!$grids){
return false;
}
// スコア降順にソートを行う
foreach ($grids as $key => $grid){
$score[$key] = $grid['score'];
}
array_multisort($score, SORT_DESC, $grids);
// 座標を取得
$x_tmp = '';
$y_tmp = '';
// 特徴点配列
$point = array();
$i = 0;
foreach ($grids as $grid){
if($x_tmp == '' && $y_tmp == ''){
// 本番使用
$point[$i]['x1'] = $grid['x1'];
$point[$i]['y1'] = $grid['y1'];
$point[$i]['x2'] = $grid['x2'];
$point[$i]['y2'] = $grid['y2'];
// 比較用テンポラリー座標
$x_tmp = $grid['x1'];
$y_tmp = $grid['y1'];
$i++;
}else{
// スコアのもっとも高い特徴点との距離を測る(変換前の点を使用する)
$x_dis = $x_tmp - $grid['x1'];
$y_dis = $y_tmp - $grid['y1'];
$x_p = pow($x_dis, 2);
$y_p = pow($y_dis, 2);
$dist = sqrt($x_p + $y_p);
// 距離5以上があれば採用する ←調整の必要がある
if($dist >= 1){
$point[$i]['x1'] = $grid['x1'];
$point[$i]['y1'] = $grid['y1'];
$point[$i]['x2'] = $grid['x2'];
$point[$i]['y2'] = $grid['y2'];
$i++;
}
}
if(count($point) >= 3){
// 上位3点で終了
break;
}
}
// 特徴点配列が3点無かった場合
if(count($point) < 3){
return false;
}
// アフィアン変換の未知数
$a = '';
$b = '';
$c = '';
$d = '';
$l = '';
$m = '';
// 連立方程式を解く
$a = ($point[0]['x2'] * $point[1]['y1'] + $point[1]['x2'] * $point[2]['y1'] + $point[2]['x2'] * $point[0]['y1'] - $point[0]['x2'] * $point[2]['y1'] - $point[1]['x2'] * $point[0]['y1'] - $point[2]['x2'] * $point[1]['y1']) / ($point[0]['x1'] * $point[1]['y1'] + $point[1]['x1'] * $point[2]['y1'] + $point[2]['x1'] * $point[0]['y1'] - $point[0]['x1'] * $point[2]['y1'] - $point[1]['x1'] * $point[0]['y1'] - $point[2]['x1'] * $point[1]['y1']);
$b = ($point[0]['x1'] * $point[1]['x2'] + $point[1]['x1'] * $point[2]['x2'] + $point[2]['x1'] * $point[0]['x2'] - $point[0]['x1'] * $point[2]['x2'] - $point[1]['x1'] * $point[0]['x2'] - $point[2]['x1'] * $point[1]['x2']) / ($point[0]['x1'] * $point[1]['y1'] + $point[1]['x1'] * $point[2]['y1'] + $point[2]['x1'] * $point[0]['y1'] - $point[0]['x1'] * $point[2]['y1'] - $point[1]['x1'] * $point[0]['y1'] - $point[2]['x1'] * $point[1]['y1']);
$l = ($point[0]['x1'] * $point[1]['y1'] * $point[2]['x2'] + $point[1]['x1'] * $point[2]['y1'] * $point[0]['x2'] + $point[2]['x1'] * $point[0]['y1'] * $point[1]['x2'] - $point[0]['x1'] * $point[2]['y1'] * $point[1]['x2'] - $point[1]['x1'] * $point[0]['y1'] * $point[2]['x2'] - $point[2]['x1'] * $point[1]['y1'] * $point[0]['x2']) / ($point[0]['x1'] * $point[1]['y1'] + $point[1]['x1'] * $point[2]['y1'] + $point[2]['x1'] * $point[0]['y1'] - $point[0]['x1'] * $point[2]['y1'] - $point[1]['x1'] * $point[0]['y1'] - $point[2]['x1'] * $point[1]['y1']);
$c = ($point[0]['y2'] * $point[1]['y1'] + $point[1]['y2'] * $point[2]['y1'] + $point[2]['y2'] * $point[0]['y1'] - $point[0]['y2'] * $point[2]['y1'] - $point[1]['y2'] * $point[0]['y1'] - $point[2]['y2'] * $point[1]['y1']) / ($point[0]['x1'] * $point[1]['y1'] + $point[1]['x1'] * $point[2]['y1'] + $point[2]['x1'] * $point[0]['y1'] - $point[0]['x1'] * $point[2]['y1'] - $point[1]['x1'] * $point[0]['y1'] - $point[2]['x1'] * $point[1]['y1']);
$d = ($point[0]['x1'] * $point[1]['y2'] + $point[1]['x1'] * $point[2]['y2'] + $point[2]['x1'] * $point[0]['y2'] - $point[0]['x1'] * $point[2]['y2'] - $point[1]['x1'] * $point[0]['y2'] - $point[2]['x1'] * $point[1]['y2']) / ($point[0]['x1'] * $point[1]['y1'] + $point[1]['x1'] * $point[2]['y1'] + $point[2]['x1'] * $point[0]['y1'] - $point[0]['x1'] * $point[2]['y1'] - $point[1]['x1'] * $point[0]['y1'] - $point[2]['x1'] * $point[1]['y1']);
$m = ($point[0]['x1'] * $point[1]['y1'] * $point[2]['y2'] + $point[1]['x1'] * $point[2]['y1'] * $point[0]['y2'] + $point[2]['x1'] * $point[0]['y1'] * $point[1]['y2'] - $point[0]['x1'] * $point[2]['y1'] * $point[1]['y2'] - $point[1]['x1'] * $point[0]['y1'] * $point[2]['y2'] - $point[2]['x1'] * $point[1]['y1'] * $point[0]['y2']) / ($point[0]['x1'] * $point[1]['y1'] + $point[1]['x1'] * $point[2]['y1'] + $point[2]['x1'] * $point[0]['y1'] - $point[0]['x1'] * $point[2]['y1'] - $point[1]['x1'] * $point[0]['y1'] - $point[2]['x1'] * $point[1]['y1']);
// DBから変換前の範囲指定4点を取得
$inquiry = TInquiryTable::getInquiryById($question_id);
$left_up_xy = $inquiry->getLeftUpXy();
$right_up_xy = $inquiry->getRightUpXy();
$left_down_xy = $inquiry->getLeftDownXy();
$right_down_xy = $inquiry->getRightDownXy();
// 点が無ければ返す
if(!$left_up_xy || !$right_up_xy || !$left_down_xy|| !$right_down_xy){
return false;
} else if($left_up_xy == '' || $right_up_xy == '' || $left_down_xy == '' || $right_down_xy == ''){
return false;
}
// 頂点毎に配列に入れる
$left_up = explode('_', $left_up_xy);
$right_up = explode('_', $right_up_xy);
$left_down = explode('_', $left_down_xy);
$right_down = explode('_', $right_down_xy);
// アフィアン変換実施
$left_up_affin_x = $a * $left_up[0] + $b * $left_up[1] + $l;
$left_up_affin_y = $c * $left_up[0] + $d * $left_up[1] + $m;
$right_up_affin_x = $a * $right_up[0] + $b * $right_up[1] + $l;
$right_up_affin_y = $c * $right_up[0] + $d * $right_up[1] + $m;
$left_down_affin_x = $a * $left_down[0] + $b * $left_down[1] + $l;
$left_down_affin_y = $c * $left_down[0] + $d * $left_down[1] + $m;
$right_down_affin_x = $a * $right_down[0] + $b * $right_down[1] + $l;
$right_down_affin_y = $c * $right_down[0] + $d * $right_down[1] + $m;
// 4頂点の配列を作成(要素の名前で頂点の場所を判別可能)
$grids = array();
$grids['left_up']['x'] = $left_up_affin_x;
$grids['left_up']['y'] = $left_up_affin_y;
$grids['right_up']['x'] = $right_up_affin_x;
$grids['right_up']['y'] = $right_up_affin_y;
$grids['left_down']['x'] = $left_down_affin_x;
$grids['left_down']['y'] = $left_down_affin_y;
$grids['right_down']['x'] = $right_down_affin_x;
$grids['right_down']['y'] = $right_down_affin_y;
return $grids;
}
/**
* 変換前の座標系から回答登録画像の座標系へ変換
*
* @param $scale 倍率
* @param $grids 変換する頂点
*/
public static function scaleArea($scale, $grids){
// 回答画像とマッチング用画像の比率で変換
$grids['left_up']['x'] = $grids['left_up']['x'] * $scale;
$grids['left_up']['y'] = $grids['left_up']['y'] * $scale;
$grids['right_up']['x'] = $grids['right_up']['x'] * $scale;
$grids['right_up']['y'] = $grids['right_up']['y'] * $scale;
$grids['left_down']['x'] = $grids['left_down']['x'] * $scale;
$grids['left_down']['y'] = $grids['left_down']['y'] * $scale;;
$grids['right_down']['x'] = $grids['right_down']['x'] * $scale;
$grids['right_down']['y'] = $grids['right_down']['y'] * $scale;
return $grids;
}
/**
* 指定した4点が囲む領域内に存在する回答IDを返す
*
* @param $im_search_id 画像検索マスターID
* @param $grids 領域をつくる4点
* @param $inverse_cordinate 見つかった回答の点を質問画像上に変換するための頂点
* @param $scale 質問から回答への座標の倍率
*
* @return 回答情報
*/
public static function getSearchResultInArea($im_search_id, $grids, $inverse_conrdinate, $scale){
// 回答の中心点を取得する
$m_im_search_points = MImSearchPointTable::getImSearchPointQnumberAscByImSearchId($im_search_id);
// 1つも無ければ返す
if(!$m_im_search_points){
return false;
}elseif(count($m_im_search_points) == 0){
return false;
}
// 4点の座標を整理
$a_x = $grids['left_up']['x'];
$a_y = $grids['left_up']['y'];
$b_x = $grids['left_down']['x'];
$b_y = $grids['left_down']['y'];
$c_x = $grids['right_down']['x'];
$c_y = $grids['right_down']['y'];
$d_x = $grids['right_up']['x'];
$d_y = $grids['right_up']['y'];
// 回答の中心点を内包しているかどうか
$incluse_points = array();
// 問題の表示番号
$q_cnt = 0;
$equal_q_cnt = 0;
$q_num_buf = 0;
foreach($m_im_search_points as $point){
if($q_num_buf < $point['q_number']){
// 質問番号が前回より大きい場合、バッファ番号の入れ替え
$q_num_buf = $point['q_number'];
// 同じ質問内のカウンターを初期化
$equal_q_cnt = 0;
// 質問カウンターをインクリメント
$q_cnt++;
}elseif($q_num_buf == $point['q_number']){
// 質問番号が前回と同じなら、同じ質問内のカウンターをインクリメント
$equal_q_cnt++;
}
// 質問中心点
$p_x = $point['center_x'];
$p_y = $point['center_y'];
// NULLや空の場合の対応
if(empty($p_x) || empty($p_y)){
continue;
}
//PA
$pa_x = $a_x - $p_x;
$pa_y = $a_y - $p_y;
//PB
$pb_x = $b_x - $p_x;
$pb_y = $b_y - $p_y;
//PC
$pc_x = $c_x - $p_x;
$pc_y = $c_y - $p_y;
//PD
$pd_x = $d_x - $p_x;
$pd_y = $d_y - $p_y;
// AB
$ab_x = $b_x - $a_x;
$ab_y = $b_y - $a_y;
// BC
$bc_x = $c_x - $b_x;
$bc_y = $c_y - $b_y;
// CD
$cd_x = $d_x - $c_x;
$cd_y = $d_y - $c_y;
// DA
$da_x = $a_x - $d_x;
$da_y = $a_y - $d_y;
// 外積の計算
$vector_product_outer1 = $pa_x * $ab_y - $pa_y * $ab_x;
$vector_product_outer2 = $pb_x * $bc_y - $pb_y * $bc_x;
$vector_product_outer3 = $pc_x * $cd_y - $pc_y * $cd_x;
$vector_product_outer4 = $pd_x * $da_y - $pd_y * $da_x;
// 符号の一致
if(($vector_product_outer1 >= 0) && ($vector_product_outer2 >= 0) && ($vector_product_outer3 >= 0) && ($vector_product_outer4 >= 0)) {
if(empty($point['movie_id'])){
$point['search_type'] = sfConfig::get('app_matching_success_nomovie');
}
$incluse_points[$q_cnt][$equal_q_cnt]['point']= $point;
$incluse_points[$q_cnt][$equal_q_cnt]['display_point'] = self::getHomographyTransformCoordinateScale($p_x, $p_y, $inverse_conrdinate, $scale);
}elseif(($vector_product_outer1 <= 0) && ($vector_product_outer2 <= 0) && ($vector_product_outer3 <= 0) && ($vector_product_outer4 <= 0)){
if(empty($point['movie_id'])){
$point['search_type'] = sfConfig::get('app_matching_success_nomovie');
}
$incluse_points[$q_cnt][$equal_q_cnt]['point'] = $point;
$incluse_points[$q_cnt][$equal_q_cnt]['display_point'] = self::getHomographyTransformCoordinateScale($p_x, $p_y, $inverse_conrdinate, $scale);
}
}
return $incluse_points;
}
/**
* マッチング画像の領域をホモグラフィ変換して座標を返す関数
*
* @param $question_id ユーザ選択領域を取得する質問ID
* @param $grids ホモグラフィー行列用座標点(480×640との対応点)
*/
public static function getHomographyTransform($question_id, $grids){
$log_data = '#question_id: ' . $question_id . "\n";
// 4点を取得
// 配列のチェック
if(!$grids){
return false;
}
// 座標を取得
$x_tmp = '';
$y_tmp = '';
// 特徴点配列
$point = array();
$i = 0;
foreach ($grids as $grid){
if($x_tmp == '' && $y_tmp == ''){
// 本番使用
$point[$i]['x1'] = $grid['x1'];
$point[$i]['y1'] = $grid['y1'];
$point[$i]['x2'] = $grid['x2'];
$point[$i]['y2'] = $grid['y2'];
// 比較用テンポラリー座標
$x_tmp = $grid['x1'];
$y_tmp = $grid['y1'];
$i++;
}else{
// スコアのもっとも高い特徴点との距離を測る(変換前の点を使用する)
$x_dis = $x_tmp - $grid['x1'];
$y_dis = $y_tmp - $grid['y1'];
$x_p = pow($x_dis, 2);
$y_p = pow($y_dis, 2);
$dist = sqrt($x_p + $y_p);
// 距離3以上があれば採用する
if($dist >= 1){
$point[$i]['x1'] = $grid['x1'];
$point[$i]['y1'] = $grid['y1'];
$point[$i]['x2'] = $grid['x2'];
$point[$i]['y2'] = $grid['y2'];
$i++;
}
}
if(count($point) >= 4){
// 上位4点で終了
break;
}
}
// 特徴点配列が4点無かった場合
if(count($point) < 4){
return false;
}
// 既知の8×8の行列Gを作成する
$array_h0 = array();
$array_h1 = array();
$array_h2 = array();
$array_h3 = array();
$array_h4 = array();
$array_h5 = array();
$array_h6 = array();
$array_h7 = array();
$array_h0 = array($point[0]['x1'], $point[0]['y1'], 1, 0, 0, 0, -($point[0]['x1'] * $point[0]['x2']), -($point[0]['y1'] * $point[0]['x2']));
$array_h1 = array(0, 0, 0, $point[0]['x1'], $point[0]['y1'], 1, -($point[0]['x1'] * $point[0]['y2']), -($point[0]['y1'] * $point[0]['y2']));
$array_h2 = array($point[1]['x1'], $point[1]['y1'], 1, 0, 0, 0, -($point[1]['x1'] * $point[1]['x2']), -($point[1]['y1'] * $point[1]['x2']));
$array_h3 = array(0, 0, 0, $point[1]['x1'], $point[1]['y1'], 1, -($point[1]['x1'] * $point[1]['y2']), -($point[1]['y1'] * $point[1]['y2']));
$array_h4 = array($point[2]['x1'], $point[2]['y1'], 1, 0, 0, 0, -($point[2]['x1'] * $point[2]['x2']), -($point[2]['y1'] * $point[2]['x2']));
$array_h5 = array(0, 0, 0, $point[2]['x1'], $point[2]['y1'], 1, -($point[2]['x1'] * $point[2]['y2']), -($point[2]['y1'] * $point[2]['y2']));
$array_h6 = array($point[3]['x1'], $point[3]['y1'], 1, 0, 0, 0, -($point[3]['x1'] * $point[3]['x2']), -($point[3]['y1'] * $point[3]['x2']));
$array_h7 = array(0, 0, 0, $point[3]['x1'], $point[3]['y1'], 1, -($point[3]['x1'] * $point[3]['y2']), -($point[3]['y1'] * $point[3]['y2']));
$matrix_h = array();
$matrix_h = array($array_h0, $array_h1, $array_h2, $array_h3, $array_h4, $array_h5, $array_h6, $array_h7);
// 逆行列の入れ物
$matrix_h_inv = array();
// テンポラリー
$buf;
// 次元数
$n = 8;
// 行、列番号
$i;
$j;
$k;
// 逆行列を求める
for($i=0; $i<$n; $i++){
for($j=0;$j<$n;$j++){
$matrix_h_inv[$i][$j] = ($i==$j?1:0);
}
}
for($i=0; $i<$n; $i++){
if($matrix_h[$i][$i] == 0){
$buf = 1;
}else{
$buf = 1 / $matrix_h[$i][$i];
}
for($j=0; $j<$n; $j++){
$matrix_h[$i][$j] *= $buf;
$matrix_h_inv[$i][$j] *= $buf;
}
for($j=0; $j<$n; $j++){
if($i != $j){
$buf = $matrix_h[$j][$i];
for($k=0; $k<$n; $k++){
$matrix_h[$j][$k] -= $matrix_h[$i][$k] * $buf;
$matrix_h_inv[$j][$k] -= $matrix_h_inv[$i][$k] * $buf;
}
}
}
}
// ホモグラフィ係数
$h0 = ($matrix_h_inv[0][0] * $point[0]['x2']) + ($matrix_h_inv[0][1] * $point[0]['y2']) +
($matrix_h_inv[0][2] * $point[1]['x2']) + ($matrix_h_inv[0][3] * $point[1]['y2']) +
($matrix_h_inv[0][4] * $point[2]['x2']) + ($matrix_h_inv[0][5] * $point[2]['y2']) +
($matrix_h_inv[0][6] * $point[3]['x2']) + ($matrix_h_inv[0][7] * $point[3]['y2']);
$h1 = ($matrix_h_inv[1][0] * $point[0]['x2']) + ($matrix_h_inv[1][1] * $point[0]['y2']) +
($matrix_h_inv[1][2] * $point[1]['x2']) + ($matrix_h_inv[1][3] * $point[1]['y2']) +
($matrix_h_inv[1][4] * $point[2]['x2']) + ($matrix_h_inv[1][5] * $point[2]['y2']) +
($matrix_h_inv[1][6] * $point[3]['x2']) + ($matrix_h_inv[1][7] * $point[3]['y2']);
$h2 = ($matrix_h_inv[2][0] * $point[0]['x2']) + ($matrix_h_inv[2][1] * $point[0]['y2']) +
($matrix_h_inv[2][2] * $point[1]['x2']) + ($matrix_h_inv[2][3] * $point[1]['y2']) +
($matrix_h_inv[2][4] * $point[2]['x2']) + ($matrix_h_inv[2][5] * $point[2]['y2']) +
($matrix_h_inv[2][6] * $point[3]['x2']) + ($matrix_h_inv[2][7] * $point[3]['y2']);
$h3 = ($matrix_h_inv[3][0] * $point[0]['x2']) + ($matrix_h_inv[3][1] * $point[0]['y2']) +
($matrix_h_inv[3][2] * $point[1]['x2']) + ($matrix_h_inv[3][3] * $point[1]['y2']) +
($matrix_h_inv[3][4] * $point[2]['x2']) + ($matrix_h_inv[3][5] * $point[2]['y2']) +
($matrix_h_inv[3][6] * $point[3]['x2']) + ($matrix_h_inv[3][7] * $point[3]['y2']);
$h4 = ($matrix_h_inv[4][0] * $point[0]['x2']) + ($matrix_h_inv[4][1] * $point[0]['y2']) +
($matrix_h_inv[4][2] * $point[1]['x2']) + ($matrix_h_inv[4][3] * $point[1]['y2']) +
($matrix_h_inv[4][4] * $point[2]['x2']) + ($matrix_h_inv[4][5] * $point[2]['y2']) +
($matrix_h_inv[4][6] * $point[3]['x2']) + ($matrix_h_inv[4][7] * $point[3]['y2']);
$h5 = ($matrix_h_inv[5][0] * $point[0]['x2']) + ($matrix_h_inv[5][1] * $point[0]['y2']) +
($matrix_h_inv[5][2] * $point[1]['x2']) + ($matrix_h_inv[5][3] * $point[1]['y2']) +
($matrix_h_inv[5][4] * $point[2]['x2']) + ($matrix_h_inv[5][5] * $point[2]['y2']) +
($matrix_h_inv[5][6] * $point[3]['x2']) + ($matrix_h_inv[5][7] * $point[3]['y2']);
$h6 = ($matrix_h_inv[6][0] * $point[0]['x2']) + ($matrix_h_inv[6][1] * $point[0]['y2']) +
($matrix_h_inv[6][2] * $point[1]['x2']) + ($matrix_h_inv[6][3] * $point[1]['y2']) +
($matrix_h_inv[6][4] * $point[2]['x2']) + ($matrix_h_inv[6][5] * $point[2]['y2']) +
($matrix_h_inv[6][6] * $point[3]['x2']) + ($matrix_h_inv[6][7] * $point[3]['y2']);
$h7 = ($matrix_h_inv[7][0] * $point[0]['x2']) + ($matrix_h_inv[7][1] * $point[0]['y2']) +
($matrix_h_inv[7][2] * $point[1]['x2']) + ($matrix_h_inv[7][3] * $point[1]['y2']) +
($matrix_h_inv[7][4] * $point[2]['x2']) + ($matrix_h_inv[7][5] * $point[2]['y2']) +
($matrix_h_inv[7][6] * $point[3]['x2']) + ($matrix_h_inv[7][7] * $point[3]['y2']);
$log_data .= '##' . 'homography transform' . "\n";
$log_data .= $h0 . ', ';
$log_data .= $h1 . ', ';
$log_data .= $h2 . ', ';
$log_data .= $h3 . ', ';
$log_data .= $h4 . ', ';
$log_data .= $h5 . ', ';
$log_data .= $h6 . ', ';
$log_data .= $h7 . ', ' . "\n";
// DBから変換前の範囲指定4点を取得
$inquiry = TQuestionTable::getQuestionById($question_id);
$left_up_xy = $inquiry->getLeftUpXy();
$right_up_xy = $inquiry->getRightUpXy();
$left_down_xy = $inquiry->getLeftDownXy();
$right_down_xy = $inquiry->getRightDownXy();
// 点が無ければ返す
if(!$left_up_xy || !$right_up_xy || !$left_down_xy|| !$right_down_xy){
return false;
} else if($left_up_xy == '' || $right_up_xy == '' || $left_down_xy == '' || $right_down_xy == ''){
return false;
}
// 頂点毎に配列に入れる
$left_up = explode('_', $left_up_xy);
$right_up = explode('_', $right_up_xy);
$left_down = explode('_', $left_down_xy);
$right_down = explode('_', $right_down_xy);
// ホモグラフィ変換
// 左上
$scale = $h6 * $left_up[0] + $h7 * $left_up[1] + 1;
if($scale == 0){
return false;
}
$left_up_homography_x = ($h0 * $left_up[0] + $h1 * $left_up[1] + $h2) / $scale;
$left_up_homography_y = ($h3 * $left_up[0] + $h4 * $left_up[1] + $h5) / $scale;
// 左下
$scale = $h6 * $left_down[0] + $h7 * $left_down[1] + 1;
if($scale == 0){
return false;
}
$left_down_homography_x = ($h0 * $left_down[0] + $h1 * $left_down[1] + $h2) / $scale;
$left_down_homography_y = ($h3 * $left_down[0] + $h4 * $left_down[1] + $h5) / $scale;
// 右上
$scale = $h6 * $right_up[0] + $h7 * $right_up[1] + 1;
if($scale == 0){
return false;
}
$right_up_homography_x = ($h0 * $right_up[0] + $h1 * $right_up[1] + $h2) / $scale;
$right_up_homography_y = ($h3 * $right_up[0] + $h4 * $right_up[1] + $h5) / $scale;
// 右下
$scale = $h6 * $right_down[0] + $h7 * $right_down[1] + 1;
if($scale == 0){
return false;
}
$right_down_homography_x = ($h0 * $right_down[0] + $h1 * $right_down[1] + $h2) / $scale;
$right_down_homography_y = ($h3 * $right_down[0] + $h4 * $right_down[1] + $h5) / $scale;
$grids = array();
$grids['left_up']['x'] = $left_up_homography_x;
$grids['left_up']['y'] = $left_up_homography_y;
$grids['right_up']['x'] = $right_up_homography_x;
$grids['right_up']['y'] = $right_up_homography_y;
$grids['left_down']['x'] = $left_down_homography_x;
$grids['left_down']['y'] = $left_down_homography_y;
$grids['right_down']['x'] = $right_down_homography_x;
$grids['right_down']['y'] = $right_down_homography_y;
$log_data .= '##' . 'homography transform coordinate' . "\n";
$log_data .= $left_up_homography_x . ', ';
$log_data .= $left_up_homography_y . ', ';
$log_data .= $right_up_homography_x . ', ';
$log_data .= $right_up_homography_y . ', ';
$log_data .= $left_down_homography_x . ', ';
$log_data .= $left_down_homography_y . ', ';
$log_data .= $right_down_homography_x . ', ';
$log_data .= $right_down_homography_y . ', ';
if(sfConfig::get('sf_environment') != 'prod') {
// ログ出力
pjLogger::getInstance()->debug($log_data, 'matching');
}
return($grids);
}
/**
* 与えられた複数の角度が180度より小さいか(凸か)
*
* @param $data 角度の配列
* @return true:全ての角度が閾値内の場合、false:少なくとも1つの角度が閾値外の場合
*/
public static function isConvex($data){
// 角度の閾値との比較
for($i=0; $i < count($data); $i++){
$result = self::filterConvex($data[$i]);
if(!$result){
return false;
}
}
return true;
}
/**
* 角度の閾値検査
*
* @param $value 角度
* @return true:閾値内の場合、false:閾値外の場合
*/
public static function filterConvex($value){
$max = sfConfig::get('app_api_convex_max');
$min = sfConfig::get('app_api_convex_min');
if($min < $value && $value < $max){
return true;
}
return false;
}
/**
* 周囲の閾値検査
*
* @param $Perimeter 周囲長
*/
public static function isPerimeterInThreshold($Perimeter){
$max = sfConfig::get('app_api_perimeter_max');
$min = sfConfig::get('app_api_perimeter_min');
// 周囲の閾値との比較
if($min < $Perimeter && $Perimeter < $max){
return true;
}
return false;
}
/**
* 対辺のベクトルとの内積が閾値に含まれているか
*
* @param $inners 内角値
*/
public static function isInnerInThreshold($inners){
$min = sfConfig::get('app_api_inner_min');
foreach($inners as $inner){
// 周囲の閾値との比較
if($inner < $min){
return false;
}
}
return true;
}
/**
* 周囲長を取得する
*
* @param $grids 左上の点から時計回りに順に座標点を格納した配列
*/
public static function getPerimeter($grids){
// 配列のチェック
if(!$grids){
return false;
}
// 頂点数
$count = count($grids);
// 周囲長
$length = 0;
for($i = 0; $i < $count; $i++){
$length += self::getDistanceToNext($i, $grids);
}
return $length;
}
/**
* 指定した点と時計周りに進んだ次の点を結んだ辺の傾き - 隣合わない辺の傾き
*
* @param $grids 傾きの差
*/
public static function getSlopeDiff($num, $grids){
$count = count($grids);
$max_grid_num = $count - 1;
if($num < 0 || $max_grid_num < $num){
// 指定された頂点の番号が不正
return false;
}
$next_num = ($num + 2) % $count;
$slope1 = self::getSlope($num, $grids);
$slope2 = self::getSlope($next_num, $grids);
return abs(abs($slope1) - abs($slope2));
}
/**
* 指定した座標点と時計周りに進んだ次の点を結んだ辺と隣合わない辺の内積計算
*
* @param $num 座標点の番号
* @param $grids 左上の点から時計回りに順に座標点を格納した配列
* @return 内積値(座標が足りない場合falseを返す)
*/
public static function getInner($num, $grids){
// 配列のチェック
if(!$grids){
return false;
}
$count = count($grids);
$max_grid_num = $count - 1;
if($num < 0 || $max_grid_num < $num){
// 指定された頂点の番号が不正
return false;
}
$next_num = ($num + 1) % $count;
$ini_point_x = $grids[$num]['x2'];
$ini_point_y = $grids[$num]['y2'];
$term_point_x = $grids[$next_num]['x2'];
$term_point_y = $grids[$next_num]['y2'];
$vector_x = $term_point_x - $ini_point_x;
$vector_y = $term_point_y - $ini_point_y;
// 隣合わない辺
$opp_num = ($num + 2) % $count;
$term_opp_num = ($opp_num + 1) % $count;
$ini_opp_point_x = $grids[$opp_num]['x2'];
$ini_opp_point_y = $grids[$opp_num]['y2'];
$term_opp_point_x = $grids[$term_opp_num]['x2'];
$term_opp_point_y = $grids[$term_opp_num]['y2'];
$opp_vector_x = $ini_opp_point_x - $term_opp_point_x;
$opp_vector_y = $ini_opp_point_y - $term_opp_point_y;
// 正規化&内積
$scale = sqrt(pow($vector_x, 2) + pow($vector_y, 2)) * sqrt(pow($opp_vector_x, 2) + pow($opp_vector_y, 2));
if($scale == 0){
$scale = 1;
}
$inner_v = ($vector_x * $opp_vector_x + $vector_y * $opp_vector_y) / $scale;
return $inner_v;
}
/**
* 指定した座標点と時計回りに進んだ次の点を結んだ直線の傾きを取得する
*
* @param $num 指定した座標点
* @param $grids 左上の点から時計回りに順に座標点を格納した配列
*/
public static function getSlope($num, $grids){
// 配列のチェック
if(!$grids){
return false;
}
$count = count($grids);
$max_grid_num = $count - 1;
if($num < 0 || $max_grid_num < $num){
// 指定された頂点の番号が不正
return false;
}
$next_num = ($num + 1) % $count;
$ini_point_x = $grids[$num]['x2'];
$ini_point_y = $grids[$num]['y2'];
$term_point_x = $grids[$next_num]['x2'];
$term_point_y = $grids[$next_num]['y2'];
$deno = $term_point_x - $ini_point_x;
if($deno == 0){
return $deno = 1;
}
return ($term_point_y - $ini_point_y) / $deno;
}
/**
* 指定した座標点と隣合う点を結んだ直線の内角を取得する
*
* @param $num 座標点の番号
* @param $grids 座標点の配列(含まれる座標の数がnのときn角形とする)
*/
public static function getInteriorAngle($num, $grids){
if(!$grids){
return false;
}
// 指定点
$count = count($grids);
$max_grid_num = $count - 1;
if($num < 0 || $max_grid_num < $num){
// 指定された頂点の番号が不正
return false;
}
$center_grid_x = $grids[$num]['x2'];
$center_grid_y = $grids[$num]['y2'];
// 前、後の点
$buff = $num - 1;
if($buff < 0){
while($buff < 0){
// 負だったら正になるまで点の数を足す
$buff += $count;
}
}
$previous_num = $buff % $count;
$next_num = ($num + 1) % $count;
// 座標
$previous_grid_x = $grids[$previous_num]['x2'];
$previous_grid_y = $grids[$previous_num]['y2'];
$next_grid_x = $grids[$next_num]['x2'];
$next_grid_y = $grids[$next_num]['y2'];
// ベクトル
$v_pre_cen_x = $previous_grid_x - $center_grid_x;
$v_pre_cen_y = $previous_grid_y - $center_grid_y;
$v_next_cen_x = $next_grid_x - $center_grid_x;
$v_next_cen_y = $next_grid_y - $center_grid_y;
$v_next_scale = sqrt(pow($v_next_cen_x,2) + pow($v_next_cen_y, 2));
// 内積
$inner_v = $v_pre_cen_x * $v_next_cen_x + $v_pre_cen_y * $v_next_cen_y;
// 外積
$outer_v = $v_pre_cen_x * $v_next_cen_y - $v_pre_cen_y * $v_next_cen_x;
// 逆tan
$atan = atan2($outer_v, $inner_v);
// 度に変換
$deg = abs(rad2deg($atan));
if(rad2deg($atan) > 0){
$deg += 180;
}
return $deg;
}
/**
* 指定した座標点と次の座標点までのユークリッド距離を取得する
*
* @param $num 指定した座標点
* @param $grids 座標点の配列(含まれる座標の数がnのときn角形とする)
*/
public static function getDistanceToNext($num, $grids){
if(!$grids){
return false;
}
// 指定した番号
$count = count($grids);
$max_grid_num = $count - 1;
if($num < 0 || $max_grid_num < $num){
// 指定された頂点の番号が不正
return false;
}
$next_num = ($num + 1) % $count;
$ini_point_x = $grids[$num]['x2'];
$ini_point_y = $grids[$num]['y2'];
$term_point_x = $grids[$next_num]['x2'];
$term_point_y = $grids[$next_num]['y2'];
return sqrt(pow($term_point_x - $ini_point_x, 2) + pow($term_point_y - $ini_point_y, 2));
}
/**
* 与えられた複数座標の中心座標を返す
*
* @param unknown $coordinates 座標の配列(想定する配列 array[n][x or y])
* @return $center_value 座標情報(配列array[x or y])
*/
public static function getCenterCoordinate($coordinates){
// 初期化
$max_x_value = $coordinates[0]['x'];
$min_x_value = $coordinates[0]['x'];
$max_y_value = $coordinates[0]['y'];
$min_y_value = $coordinates[0]['y'];
// 座標点を含む矩形の中点を作成して返す
foreach($coordinates as $coordinate){
if($coordinate['x'] < $min_x_value){
$min_x_value = $coordinate['x'];
}
if($coordinate['y'] < $min_y_value){
$min_y_value = $coordinate['y'];
}
if($coordinate['x'] > $max_x_value){
$max_x_value = $coordinate['x'];
}
if($coordinate['y'] > $max_y_value){
$max_y_value = $coordinate['y'];
}
}
$center_value = array();
$center_value['x'] = ($max_x_value + $min_x_value) / 2;
$center_value['y'] = ($max_y_value + $min_y_value) / 2;
return $center_value;
}
/**
* 回答登録画像から質問画像の座標系へ変換してホモグラフィー変換を行う
*
* @param $coordinate_x 変換する頂点x
* @param $coordinate_y 変換する頂点y
* @param $grid_array ホモグフィー行列作成用の座標点情報[N][x1]to[x2] [y1]to[y2]
* @param $scale 座標系の倍率
*/
public static function getHomographyTransformCoordinateScale($coordinate_x, $coordinate_y, $grids, $scale){
return self::getHomographyTransformCoordinate($coordinate_x / $scale, $coordinate_y / $scale, $grids);
}
/**
* 与えられた頂点をホモグラフィー変換する
*
* @param $coordinate_x 変換する頂点x
* @param $coordinate_y 変換する頂点y
* @param $grid_array ホモグフィー行列作成用の座標点情報[N][x1]to[x2] [y1]to[y2]
*
*/
public static function getHomographyTransformCoordinate($coordinate_x, $coordinate_y, $grids){
// 行列作成用座標点のチェック
if(!$grids){
return false;
}
// 座標を取得
$x_tmp = '';
$y_tmp = '';
// 特徴点配列
$point = array();
$i = 0;
foreach ($grids as $grid){
if($x_tmp == '' && $y_tmp == ''){
// 本番使用
$point[$i]['x1'] = $grid['x1'];
$point[$i]['y1'] = $grid['y1'];
$point[$i]['x2'] = $grid['x2'];
$point[$i]['y2'] = $grid['y2'];
// 比較用テンポラリー座標
$x_tmp = $grid['x1'];
$y_tmp = $grid['y1'];
$i++;
}else{
// スコアのもっとも高い特徴点との距離を測る(変換前の点を使用する)
$x_dis = $x_tmp - $grid['x1'];
$y_dis = $y_tmp - $grid['y1'];
$x_p = pow($x_dis, 2);
$y_p = pow($y_dis, 2);
$dist = sqrt($x_p + $y_p);
// 距離3以上があれば採用する
if($dist >= 1){
$point[$i]['x1'] = $grid['x1'];
$point[$i]['y1'] = $grid['y1'];
$point[$i]['x2'] = $grid['x2'];
$point[$i]['y2'] = $grid['y2'];
$i++;
}
}
if(count($point) >= 4){
// 上位4点で終了
break;
}
}
// 特徴点配列が4点無かった場合
if(count($point) < 4){
return false;
}
// 既知の8×8の行列Gを作成する
$array_h0 = array();
$array_h1 = array();
$array_h2 = array();
$array_h3 = array();
$array_h4 = array();
$array_h5 = array();
$array_h6 = array();
$array_h7 = array();
$array_h0 = array($point[0]['x1'], $point[0]['y1'], 1, 0, 0, 0, -($point[0]['x1'] * $point[0]['x2']), -($point[0]['y1'] * $point[0]['x2']));
$array_h1 = array(0, 0, 0, $point[0]['x1'], $point[0]['y1'], 1, -($point[0]['x1'] * $point[0]['y2']), -($point[0]['y1'] * $point[0]['y2']));
$array_h2 = array($point[1]['x1'], $point[1]['y1'], 1, 0, 0, 0, -($point[1]['x1'] * $point[1]['x2']), -($point[1]['y1'] * $point[1]['x2']));
$array_h3 = array(0, 0, 0, $point[1]['x1'], $point[1]['y1'], 1, -($point[1]['x1'] * $point[1]['y2']), -($point[1]['y1'] * $point[1]['y2']));
$array_h4 = array($point[2]['x1'], $point[2]['y1'], 1, 0, 0, 0, -($point[2]['x1'] * $point[2]['x2']), -($point[2]['y1'] * $point[2]['x2']));
$array_h5 = array(0, 0, 0, $point[2]['x1'], $point[2]['y1'], 1, -($point[2]['x1'] * $point[2]['y2']), -($point[2]['y1'] * $point[2]['y2']));
$array_h6 = array($point[3]['x1'], $point[3]['y1'], 1, 0, 0, 0, -($point[3]['x1'] * $point[3]['x2']), -($point[3]['y1'] * $point[3]['x2']));
$array_h7 = array(0, 0, 0, $point[3]['x1'], $point[3]['y1'], 1, -($point[3]['x1'] * $point[3]['y2']), -($point[3]['y1'] * $point[3]['y2']));
$matrix_h = array();
$matrix_h = array($array_h0, $array_h1, $array_h2, $array_h3, $array_h4, $array_h5, $array_h6, $array_h7);
// 逆行列の入れ物
$matrix_h_inv = array();
// テンポラリー
$buf;
// 次元数
$n = 8;
// 行、列番号
$i;
$j;
$k;
// 逆行列を求める
for($i=0; $i<$n; $i++){
for($j=0;$j<$n;$j++){
$matrix_h_inv[$i][$j] = ($i==$j?1:0);
}
}
for($i=0; $i<$n; $i++){
if($matrix_h[$i][$i] == 0){
$buf = 1;
}else{
$buf = 1 / $matrix_h[$i][$i];
}
for($j=0; $j<$n; $j++){
$matrix_h[$i][$j] *= $buf;
$matrix_h_inv[$i][$j] *= $buf;
}
for($j=0; $j<$n; $j++){
if($i != $j){
$buf = $matrix_h[$j][$i];
for($k=0; $k<$n; $k++){
$matrix_h[$j][$k] -= $matrix_h[$i][$k] * $buf;
$matrix_h_inv[$j][$k] -= $matrix_h_inv[$i][$k] * $buf;
}
}
}
}
// ホモグラフィ係数
$h0 = ($matrix_h_inv[0][0] * $point[0]['x2']) + ($matrix_h_inv[0][1] * $point[0]['y2']) +
($matrix_h_inv[0][2] * $point[1]['x2']) + ($matrix_h_inv[0][3] * $point[1]['y2']) +
($matrix_h_inv[0][4] * $point[2]['x2']) + ($matrix_h_inv[0][5] * $point[2]['y2']) +
($matrix_h_inv[0][6] * $point[3]['x2']) + ($matrix_h_inv[0][7] * $point[3]['y2']);
$h1 = ($matrix_h_inv[1][0] * $point[0]['x2']) + ($matrix_h_inv[1][1] * $point[0]['y2']) +
($matrix_h_inv[1][2] * $point[1]['x2']) + ($matrix_h_inv[1][3] * $point[1]['y2']) +
($matrix_h_inv[1][4] * $point[2]['x2']) + ($matrix_h_inv[1][5] * $point[2]['y2']) +
($matrix_h_inv[1][6] * $point[3]['x2']) + ($matrix_h_inv[1][7] * $point[3]['y2']);
$h2 = ($matrix_h_inv[2][0] * $point[0]['x2']) + ($matrix_h_inv[2][1] * $point[0]['y2']) +
($matrix_h_inv[2][2] * $point[1]['x2']) + ($matrix_h_inv[2][3] * $point[1]['y2']) +
($matrix_h_inv[2][4] * $point[2]['x2']) + ($matrix_h_inv[2][5] * $point[2]['y2']) +
($matrix_h_inv[2][6] * $point[3]['x2']) + ($matrix_h_inv[2][7] * $point[3]['y2']);
$h3 = ($matrix_h_inv[3][0] * $point[0]['x2']) + ($matrix_h_inv[3][1] * $point[0]['y2']) +
($matrix_h_inv[3][2] * $point[1]['x2']) + ($matrix_h_inv[3][3] * $point[1]['y2']) +
($matrix_h_inv[3][4] * $point[2]['x2']) + ($matrix_h_inv[3][5] * $point[2]['y2']) +
($matrix_h_inv[3][6] * $point[3]['x2']) + ($matrix_h_inv[3][7] * $point[3]['y2']);
$h4 = ($matrix_h_inv[4][0] * $point[0]['x2']) + ($matrix_h_inv[4][1] * $point[0]['y2']) +
($matrix_h_inv[4][2] * $point[1]['x2']) + ($matrix_h_inv[4][3] * $point[1]['y2']) +
($matrix_h_inv[4][4] * $point[2]['x2']) + ($matrix_h_inv[4][5] * $point[2]['y2']) +
($matrix_h_inv[4][6] * $point[3]['x2']) + ($matrix_h_inv[4][7] * $point[3]['y2']);
$h5 = ($matrix_h_inv[5][0] * $point[0]['x2']) + ($matrix_h_inv[5][1] * $point[0]['y2']) +
($matrix_h_inv[5][2] * $point[1]['x2']) + ($matrix_h_inv[5][3] * $point[1]['y2']) +
($matrix_h_inv[5][4] * $point[2]['x2']) + ($matrix_h_inv[5][5] * $point[2]['y2']) +
($matrix_h_inv[5][6] * $point[3]['x2']) + ($matrix_h_inv[5][7] * $point[3]['y2']);
$h6 = ($matrix_h_inv[6][0] * $point[0]['x2']) + ($matrix_h_inv[6][1] * $point[0]['y2']) +
($matrix_h_inv[6][2] * $point[1]['x2']) + ($matrix_h_inv[6][3] * $point[1]['y2']) +
($matrix_h_inv[6][4] * $point[2]['x2']) + ($matrix_h_inv[6][5] * $point[2]['y2']) +
($matrix_h_inv[6][6] * $point[3]['x2']) + ($matrix_h_inv[6][7] * $point[3]['y2']);
$h7 = ($matrix_h_inv[7][0] * $point[0]['x2']) + ($matrix_h_inv[7][1] * $point[0]['y2']) +
($matrix_h_inv[7][2] * $point[1]['x2']) + ($matrix_h_inv[7][3] * $point[1]['y2']) +
($matrix_h_inv[7][4] * $point[2]['x2']) + ($matrix_h_inv[7][5] * $point[2]['y2']) +
($matrix_h_inv[7][6] * $point[3]['x2']) + ($matrix_h_inv[7][7] * $point[3]['y2']);
// ホモグラフィ変換
$scale = $h6 * $coordinate_x + $h7 * $coordinate_y + 1;
if($scale == 0){
return false;
}
$coordinate_homography_x = ($h0 * $coordinate_x + $h1 * $coordinate_y + $h2) / $scale;
$coordinate_homography_y = ($h3 * $coordinate_x + $h4 * $coordinate_y + $h5) / $scale;
$converted = array();
$converted['x'] = $coordinate_homography_x;
$converted['y'] = $coordinate_homography_y;
return($converted);
}
/**
* 与えられた座標点の共分散行列の固有値を返す
*
*/
public static function getVarianceCovarianceMatrix($grids){
// 4点を取得
// 配列のチェック
if(!$grids){
return false;
}
// 特徴点配列
$point = array();
$i = 0;
foreach ($grids as $grid){
$point[$i]['x'] = $grid['x2'];
$point[$i]['y'] = $grid['y2'];
$i++;
}
// 特徴点配列が4点無かった場合返す
if(count($point) < 4){
return false;
}
$variance = array();
// 平均
$mx = ( $point[0]['x'] + $point[1]['x'] + $point[2]['x'] + $point[3]['x'] ) / count($point);
$my = ( $point[0]['y'] + $point[1]['y'] + $point[2]['y'] + $point[3]['y'] ) / count($point);
// 標準偏差
$gapx = 0;
$gapy = 0;
foreach ($grids as $greid){
$gapx += pow($grid['x2'] - $mx, 2);
$gapy += pow($grid['y2'] - $my, 2);
}
$deltax = sqrt($gapx / count($point));
$deltay = sqrt($gapy / count($point));
$variance['deltax'] = $deltax;
$variance['deltay'] = $deltay;
// 正規化行列
$vector0 = 1 / $deltax;
$vector1 = 1 / $deltay;
$normalizepoints = array();
$j = 0;
foreach ($grids as $grid){
$normalizex = $vector0 * $grid['x2'];
$normalizey = $vector1 * $grid['y2'];
$normalizepoints[$j]['x'] = $normalizex;
$normalizepoints[$j]['y'] = $normalizey;
$j++;
}
// 正規化行列の平均
$nmx = ( $normalizepoints[0]['x'] + $normalizepoints[1]['x'] + $normalizepoints[2]['x'] + $normalizepoints[3]['x'] ) / count($normalizepoints);
$nmy = ( $normalizepoints[0]['y'] + $normalizepoints[1]['y'] + $normalizepoints[2]['y'] + $normalizepoints[3]['y'] ) / count($normalizepoints);
// 共分散行列
$tmp_conv_xx = 0;
$tmp_conv_xy = 0;
$tmp_conv_yy = 0;
foreach ($normalizepoints as $normalizepoint){
$tmp_conv_xx += pow($normalizepoint['x'] - $nmx, 2);
$tmp_conv_xy += ($normalizepoint['x'] - $nmx) * ($normalizepoint['y'] - $nmy);
$tmp_conv_yy += pow($normalizepoint['y'] - $nmy, 2);
}
$conv_xx = $tmp_conv_xx / count($normalizepoints);
$conv_xy = $tmp_conv_xy / count($normalizepoints);
$conv_yy = $tmp_conv_yy / count($normalizepoints);
// 固有値
$a = 1;
$b = - ($conv_yy + $conv_xx);
$c = $conv_xx * $conv_yy - pow($conv_xy, 2);
$root1 = (-$b + sqrt(pow($b, 2) - 4 * $a * $c)) / (2 * $a);
$root2 = (-$b - sqrt(pow($b, 2) - 4 * $a * $c)) / (2 * $a);
if($root1 > $root2){
$variance['vev'] = $root1;
}else{
$variance['vev'] = $root2;
}
return $variance;
}
/**
* 矩形のアスペクト比を取得する
*
*/
public static function getAspectRatio($grids){
// 4点を取得
// 配列のチェック
if(!$grids){
return false;
}
// 特徴点配列
$point = array();
$i = 0;
foreach ($grids as $grid){
$point[$i]['x'] = $grid['x2'];
$point[$i]['y'] = $grid['y2'];
$i++;
}
// 特徴点配列が4点無かった場合返す
if(count($point) < 4){
return false;
}
$width = self::getDistanceToNext(0, $grids) + self::getDistanceToNext(2, $grids);
$height = self::getDistanceToNext(1, $grids) + self::getDistanceToNext(3, $grids);
if ($height == 0){
return 0;
}
return $width / $height;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment