モザイク

今回は画像の特定の部分を隠蔽するために使用するモザイクフィルタを取り上げます。アルゴリズムなんてレベルの話ではなく、指定されたブロックサイズに従ってピクセルを切り出して輝度別に平均を取り、ブロックを平均値で塗りつぶすだけの単純な作業です。

今回はブロックサイズを自由に設定できて、モザイクをかける範囲を直接指定できる実用的な関数を作りたいと思います。
WSizeとHSizeでモザイクのブロックサイズを指定し、Clipでモザイクをかけるクリッピングエリアを指定します。

procedure Mozaic(Bitmap: TBitmap; WSize, HSize: Integer; Clip: TRect);
var
  X, Y, I, J: Integer;
  xR, xG, xB, XX, YY: Integer;
  Cnt, BlockW, BlockH: Integer;
  pSrcCache: PCacheLines;
  pSrcLine: PLine24;
begin
  if ((WSize < 1) or (HSize < 1)) or ((WSize = 1) and (HSize = 1)) then Exit;

  { クリッピングエリアの最適化 }
  with Clip do
  begin
    if (Left = Right) or (Top = Bottom) then Exit;
    if Left > Right then
    begin
      I     := Left;
      Left  := Right;
      Right := I;
    end;
    if Top > Bottom then
    begin
      I      := Top;
      Top    := Bottom;
      Bottom := I;
    end;
    if Left < 0 then Left := 0;
    if Top  < 0 then Top  := 0;
    if Right  >= Bitmap.Width  then Right  := Bitmap.Width;
    if Bottom >= Bitmap.Height then Bottom := Bitmap.Height;

    { ブロック数を計算する }
    BlockW := (Right - Left) div WSize;
    BlockH := (Bottom - Top) div HSize;
  end;

  Bitmap.PixelFormat := pf24bit;
  pSrcCache := GetCacheLines(Bitmap);//全ラインのキャッシュ
  try
    for Y := 0 to BlockH do
    begin
      YY := Y * HSize + Clip.Top;
      for X := 0 to BlockW do
      begin
        XX := X * WSize + Clip.Left;
        xR := 0;
        xG := 0;
        xB := 0;
        Cnt := 0;
        { ピクセルを切り出して輝度を加算する }
        for I := YY to YY+HSize -1 do
        begin
          if I >= Clip.Bottom then Continue;
          pSrcLine := pSrcCache^[I];
          for J := XX to XX+WSize -1 do
          begin
            if J >= Clip.Right then Continue;
            with pSrcLine^[J] do
            begin
              Inc(xB, B);
              Inc(xG, G);
              Inc(xR, R);
            end;
            Inc(Cnt);
          end;
        end;
        if Cnt = 0 then Continue;

        { 平均を計算 }
        xR := Trunc(xR / Cnt + 0.5);
        xG := Trunc(xG / Cnt + 0.5);
        xB := Trunc(xB / Cnt + 0.5);
        if xR > 255 then xR := 255 else if xR < 0 then xR := 0;
        if xG > 255 then xG := 255 else if xG < 0 then xG := 0;
        if xB > 255 then xB := 255 else if xB < 0 then xB := 0;

        { ブロックの塗り潰し }
        for I := YY to YY+HSize -1 do
        begin
          if I >= Clip.Bottom then Continue;
          pSrcLine := pSrcCache^[I];
          for J := XX to XX+WSize -1 do
          begin
            if J >= Clip.Right then Continue;
            with pSrcLine^[J] do
            begin
              B := xB;
              G := xG;
              R := xR;
            end;
          end;
        end;
      end;
    end;
  finally
    FreeMem(pSrcCache);
  end;
end;

Copyright 2001 Rinka Kouzuki All Rights Reserved.