21. září bude sraz! Od 18.00 v restauraci Tradice v Praze u Anděla
Autor Zpráva
Fleretak
Profil *
Mám obrázek, do kterého bych potřeboval určité části rozmazat. Na internetu jsem vygooglil blur efekt v PHP. Vytváří vždy čtverec či obdélník. Rád bych ale měl možnost i natočení (rotace). Zkoušel jsem tam vložit rotate script, ale bohužel otočená část má vždy černé pozadí.
Proto se obracím na toto fórum, zda-li by mně s tím někdo neporadil.

$x = 278;
$y = 420;
$width = 210;
$height = 140;

$img1 = imagecreatefromjpeg('webkamera/aktualni_nabrezi.jpg'); // load source
$img2 = imagecreatetruecolor($width, $height); // create img2 for selection
  imagecopy($img2, $img1, 0, 0, $x, $y, $width, $height); // copy selection to img2
  $matrix = array ( array ( 5 ,  5 ,  5 ), array ( 0 ,  0 ,  0 ), array ( 5 ,  5 ,  5 )); 
    for ($i =  1; $i <= 40; $i ++)  { 
      imageconvolution ($img2, $matrix, 30, 0); 
    }
  imagecopymerge($img1, $img2, $x, $y, 0, 0, $width, $height, 100);



// Show result (img1)
header('Content-Type: image/jpg');
imagejpeg($img1);

imagedestroy($img1);
imagedestroy($img2);
Alphard
Profil
Ten script fugnuje tak, že udělá (obdélníkový) výřez původního obrazu, na něj aplikuje rozostřující konvoluční masku a pak ten výřez zpět nakopíruje do původního obrazu.
Problém je v tom, že GD knihovna umí kopírovat pouze obdélníkové výřezy obrazů.

Řešení, které se nabízí, je tohle:
1. zkopírovat celý původní obrazek,
2. konvoluci provést na celém zkopírovaném obraze
3. pixel po pixelu si do původního obrazu zkopírovat jen to, co chcete rozmazat.
Nebude to zrovna rychlé, ale v GD knihovně mě nenapadá lepší řešení. (Hypoteticky by šlo rotovat, rozmazat a rotovat zpět, ale tím by trpěla kvalita.)

Tady jsem v rychlosti otestoval řešení pro kruhové rozmazání (definovat kruh je analyticky jednoduché :-). Analogicky k funkci $inCircle si dopište funkci $inRectangle.
function imageCopyUserShape($dst, $src, callable $isInAreaFcn) {
  for ($x = 0; $x < imagesx($dst); $x++) {
    for ($y = 0; $y < imagesy($dst); $y++) {
      if ($isInAreaFcn($x, $y)) {
        imagesetpixel($dst, $x, $y, imagecolorat($src, $x, $y));
      }
    }
  }
}

$img1 = imagecreatefromjpeg('test_image.jpg'); // load source
$img2 = imagecreatetruecolor(imagesx($img1), imagesy($img1)); // create img2 for selection
imagecopy($img2, $img1, 0, 0, 0, 0, imagesx($img1), imagesy($img1)); // copy selection to img2
$matrix = array ( array ( 5 ,  5 ,  5 ), array ( 0 ,  0 ,  0 ), array ( 5 ,  5 ,  5 ));
for ($i = 1; $i <= 40; $i++)  {
  imageconvolution ($img2, $matrix, 30, 0);
}

$inCircle = function($x, $y) {
  $center = [180, 220];
  $radius = 50;
  return (pow($x-$center[0], 2) + pow($y-$center[1], 2)) <= pow($radius, 2); };
imageCopyUserShape($img1, $img2, $inCircle);



// Show result (img1)
header('Content-Type: image/jpg');
imagejpeg($img1);

imagedestroy($img1);
imagedestroy($img2);

Doplnění:
Zvláště pro malé výřezy bude podstatně rychlejší přepsat tu logiku tak, aby se při kopírování netestoval celý obraz, ale iterovalo se pouze přes soubor hodnot. Předtím mě to nenapadlo, ale tohle je myslím dobré řešení :-) Zadefinovat kolekci bodů v obdélníku
$points = [];
for ($x; ...) {
  for ($y; ...) {
    $points[]= [$x, $y];
  }
}
tuto kolekci nechat rotovat cs.wikipedia.org/wiki/Grafick%C3%A9_transformace#Rotace_.28oto.C4.8Den.C3.AD.29 a pak zkopírovat body přesně na těchto pozicích.
Snad stačí takhle matematicky, teď nemám čas to programovat.
Alphard
Profil
Alphard [#2]:
Snad stačí takhle matematicky, teď nemám čas to programovat.
Teď jsem našel chvíli, kdy jsem se k tomu mohl vrátit. Testoval jsem na PHP 5.6, ale mělo by stačit 5.5. Konstrukce vyžadující 5.5 jsou list ve foreach a yield.

// tady jsem v podstatě jen obalil původní kód funkcí
function createBlurredCopy($src) {
  $img2 = imagecreatetruecolor(imagesx($src), imagesy($src));
  imagecopy($img2, $src, 0, 0, 0, 0, imagesx($src), imagesy($src));
  $matrix = array ( array ( 5 ,  5 ,  5 ), array ( 5 ,  5 ,  5 ), array ( 5 ,  5 ,  5 ));
  for ($i = 1; $i <= 40; $i++)  {
    imageconvolution ($img2, $matrix, 45, 0);
  }
  return $img2;
}

// afinní transformace, viz odkaz v přechozím příspěvku
function affineTransform($coef, $point) {
  $x = $coef[0][0]*$point[0] + $coef[0][1]*$point[1] + $coef[0][2];
  $y = $coef[1][0]*$point[0] + $coef[1][1]*$point[1] + $coef[1][2];
  return [$x, $y];
}

// vytvoření matice koeficientů pro afinní transformaci a vrácení funkce s generátorem bodů
function createRectanglePoints($centerX, $centerY, $width, $height, $angle) {
  $affineCoef = [
    [cos($angle), -sin($angle), $centerX],
    [sin($angle), cos($angle),  $centerY],
    [0,           0,            1]
  ];

  return function() use ($affineCoef, $width, $height) {
    for ($x = -$width/2; $x < $width/2; $x += 0.5) {
      for ($y = -$height/2; $y < $height/2; $y += 0.5) {
        list($pX, $pY) = affineTransform($affineCoef, [$x, $y]);
        yield [round($pX), round($pY)];
      }
    }
  };
}

// kopírovaní vybraných pixelů
function imageCopyPoints($dst, $src, $pointsGenerator) {
  $width = imagesx($dst);
  $height = imagesy($dst);
  foreach ($pointsGenerator() as list($x, $y)) {
    if ($x >= 0 && $x < $width && $y >= 0 && $y < $height) {
      imagesetpixel($dst, $x, $y, imagecolorat($src, $x, $y));
    }
  }
}

$img = imagecreatefromjpeg('img2/test.jpg'); // načtení zdrojového obrazu
$blurred = createBlurredCopy($img); // vytvoření rozmazané kopie
imageCopyPoints($img, $blurred, createRectanglePoints(190, 220, 90, 50, M_PI/6));
imageCopyPoints($img, $blurred, createRectanglePoints(300, 300, 100, 100, M_PI/3));
imagejpeg($img, 'blur_image.jpg');

Kubo2 [#4]:
Dík!
Kubo2
Profil
$height = imagesx($dst);
Malý preklep, správne je to $height = imagesy($dst);.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0