Find the maximal upscaled dimension while respecting a ratio
Me and a college are trying to build a method that can find the biggest size for开发者_如何学Python a selection displayed by default in an image. The selection is having a ratio.
Until now, we've tried this:
public function findBestSizeForRatio($inputW, $inputH, $ratioW, $ratioH)
{
if($inputW / $ratioW > $ratioH / $inputH){
$w = floor($inputW / $ratioW) * $ratioW;
$h = $w * $ratioH / $ratioW;
}
else{
$h = floor($inputH / $ratioH) * $ratioH;
$w = $h * $ratioW / $ratioH;
}
return array($w, $h);
}
But it fail our unit tests.
We've also tried this:
public function findBestSizeForRatio($inputW, $inputH, $ratioW, $ratioH)
{
return $this->findBestSizeInBox($ratioW*10000, $ratioH*10000, $inputW, $inputH);
}
public function findBestSizeInBox($inputW, $inputH, $boxW, $boxH)
{
if($inputW / $boxW > $inputH / $boxH){
return array($boxW, round($inputH * $boxW / $inputW));
}
else{
return array(round($inputW * $boxH / $inputH), $boxH);
}
}
It seem to work, but it's not pixel perfect. As our ratio is made of small number, we need a method that REALLY respect the ratio, even if the image is not 100% filled. And it's also really dumb to hardcode constant like *10000 because we're unable to figure out a good math formula. ;)
We've also build those PHPUnit tests, that seemed like representative to us ( So we might have forgot some cases)
/**
* @covers Img_GD::findBestSizeInBox
*/
public function testfindBestSizeForRatioReturnValidValueForEasyInput()
{
$img = new Img_GD();
//1
$iW = 400; $iH = 300; //Input image size
$rW = 4; $rH = 3; //ratio
$eW = 400; $eH = 300; //Expected
$this->assertEquals(array($eW, $eH), $img->findBestSizeForRatio($iW, $iH, $rW, $rH), "Img: {$iW} x {$iH} Ratio: {$rW} x {$rH}");
//2
$iW = 400; $iH = 3000; //Input image size
$rW = 4; $rH = 3; //ratio
$eW = 400; $eH = 300; //Expected
$this->assertEquals(array($eW, $eH), $img->findBestSizeForRatio($iW, $iH, $rW, $rH), "Img: {$iW} x {$iH} Ratio: {$rW} x {$rH}");
//3
$iW = 4000; $iH = 300; //Input image size
$rW = 4; $rH = 3; //ratio.
$eW = 400; $eH = 300; //Expected
$this->assertEquals(array($eW, $eH), $img->findBestSizeForRatio($iW, $iH, $rW, $rH), "Img: {$iW} x {$iH} Ratio: {$rW} x {$rH}");
//4
$iW = 400; $iH = 3000; //Input image size
$rW = 3; $rH = 4; //ratio
$eW = 399; $eH = 532; //Expected
$this->assertEquals(array($eW, $eH), $img->findBestSizeForRatio($iW, $iH, $rW, $rH), "Img: {$iW} x {$iH} Ratio: {$rW} x {$rH}");
//5
$iW = 4000; $iH = 300; //Input image size
$rW = 3; $rH = 4; //ratio.
$eW = 225; $eH = 300; //Expected
$this->assertEquals(array($eW, $eH), $img->findBestSizeForRatio($iW, $iH, $rW, $rH), "Img: {$iW} x {$iH} Ratio: {$rW} x {$rH}");
//6
$iW = 4000; $iH = 300; //Input image size
$rW = 3; $rH = 4; //ratio.
$eW = 225; $eH = 300; //Expected
$this->assertEquals(array($eW, $eH), $img->findBestSizeForRatio($iW, $iH, $rW, $rH), "Img: {$iW} x {$iH} Ratio: {$rW} x {$rH}");
}
/**
* @covers Img_GD::findBestSizeInBox
*/
public function testfindBestSizeForRatioReturnValidValueForNonExactInput()
{
$img = new Img_GD();
//7
$iW = 403; $iH = 302; //Input image size
$rW = 4; $rH = 3; //ratio
$eW = 400; $eH = 300; //Expected
$this->assertEquals(array($eW, $eH), $img->findBestSizeForRatio($iW, $iH, $rW, $rH), "Img: {$iW} x {$iH} Ratio: {$rW} x {$rH}");
//8
$iW = 403; $iH = 3000; //Input image size
$rW = 4; $rH = 3; //ratio
$eW = 400; $eH = 300; //Expected
$this->assertEquals(array($eW, $eH), $img->findBestSizeForRatio($iW, $iH, $rW, $rH), "Img: {$iW} x {$iH} Ratio: {$rW} x {$rH}");
//9
$iW = 4000; $iH = 302; //Input image size
$rW = 4; $rH = 3; //ratio.
$eW = 400; $eH = 300; //Expected
$this->assertEquals(array($eW, $eH), $img->findBestSizeForRatio($iW, $iH, $rW, $rH), "Img: {$iW} x {$iH} Ratio: {$rW} x {$rH}");
//10
$iW = 403; $iH = 3000; //Input image size
$rW = 3; $rH = 4; //ratio
$eW = 402; $eH = 536; //Expected
$this->assertEquals(array($eW, $eH), $img->findBestSizeForRatio($iW, $iH, $rW, $rH), "Img: {$iW} x {$iH} Ratio: {$rW} x {$rH}");
//11
$iW = 4000; $iH = 302; //Input image size
$rW = 3; $rH = 4; //ratio.
$eW = 225; $eH = 300; //Expected
$this->assertEquals(array($eW, $eH), $img->findBestSizeForRatio($iW, $iH, $rW, $rH), "Img: {$iW} x {$iH} Ratio: {$rW} x {$rH}");
//12
$iW = 4000; $iH = 302; //Input image size
$rW = 3; $rH = 4; //ratio.
$eW = 225; $eH = 300; //Expected
$this->assertEquals(array($eW, $eH), $img->findBestSizeForRatio($iW, $iH, $rW, $rH), "Img: {$iW} x {$iH} Ratio: {$rW} x {$rH}");
}
Any solution that would passes all the tests?
Try this one subtle change:
public function findBestSizeForRatio($inputW, $inputH, $ratioW, $ratioH)
{
if($inputW / $ratioW < $inputH / $ratioH){
$w = floor($inputW / $ratioW) * $ratioW;
$h = $w * $ratioH / $ratioW;
}
else{
$h = floor($inputH / $ratioH) * $ratioH;
$w = $h * $ratioW / $ratioH;
}
return array($w, $h);
}
I see you already fixed your unit tests as btilly suggested; good.
Let me be sure that I have this right. You want to take an image, and take the biggest chunk that you can which is a rectangle with exactly the specified h/w ratio.
In that case remember the rule that unit tests are code as well. When you fail your unit test, there are even odds that the mistake is in the unit test. With that in mind, your unit tests look wrong. For instance take #4. 533/400 is not in an exact 4/3 ratio. The best possible answer with that exact ratio is 532/399.
If you're willing to relax the exactness of the ratio, then you should do something like:
public function findBestSizeForRatio($inputW, $inputH, $ratioW, $ratioH)
{
if($inputW / $ratioW > $ratioH / $inputH){
$w = $inputW;
$h = round($w * $ratioH / $ratioW);
}
else{
$h = $inputH;
$h = round($h * $ratioW / $ratioH);
}
return array($w, $h);
}
精彩评论