Šta je novo?

Resize slika

voodoo_

Čuven
Učlanjen(a)
31.08.2003
Poruke
9,716
Poena
920
Bio mi je potreban resize slika za jedan moj program rađen u Delphiju, koji sam izvodio tako što sam pravio formu bez titlebara na koju sam stavljao stretchovanu TImage komponentu, ubacivao sliku i captureovao sliku forme. Ovo radi za manje slike, ali za velike ne radi jer forma neće da se risajzuje na veličinu veću od ekranske rezolucije. Znate li način (neku komponentu) koja radi resize bmp i jpeg slika (poželjno da barata direktno sa fajlovima, a ne sa TImage komponentama).
 
Našao sam rešenje. Ako još nekom zatreba, evo procedure koja vrlo uspešno radi resize slike (naravno, prethodno je potrebno postaviti width i height propertiese odredišne bitmape):

Kod:
type
  TRGBArray = array[Word] of TRGBTriple;
  pRGBArray = ^TRGBArray;

procedure SmoothResize(Src, Dst: TBitmap);
var
  x, y: Integer;
  xP, yP: Integer;
  xP2, yP2: Integer;
  SrcLine1, SrcLine2: pRGBArray;
  t3: Integer;
  z, z2, iz2: Integer;
  DstLine: pRGBArray;
  DstGap: Integer;
  w1, w2, w3, w4: Integer;
begin
  Src.PixelFormat := pf24Bit;
  Dst.PixelFormat := pf24Bit;

  if (Src.Width = Dst.Width) and (Src.Height = Dst.Height) then
    Dst.Assign(Src)
  else
  begin
    DstLine := Dst.ScanLine[0];
    DstGap  := Integer(Dst.ScanLine[1]) - Integer(DstLine);

    xP2 := MulDiv(pred(Src.Width), $10000, Dst.Width);
    yP2 := MulDiv(pred(Src.Height), $10000, Dst.Height);
    yP  := 0;

    for y := 0 to pred(Dst.Height) do
    begin
      xP := 0;

      SrcLine1 := Src.ScanLine[yP shr 16];

      if (yP shr 16 < pred(Src.Height)) then
        SrcLine2 := Src.ScanLine[succ(yP shr 16)]
      else
        SrcLine2 := Src.ScanLine[yP shr 16];

      z2  := succ(yP and $FFFF);
      iz2 := succ((not yp) and $FFFF);
      for x := 0 to pred(Dst.Width) do
      begin
        t3 := xP shr 16;
        z  := xP and $FFFF;
        w2 := MulDiv(z, iz2, $10000);
        w1 := iz2 - w2;
        w4 := MulDiv(z, z2, $10000);
        w3 := z2 - w4;
        DstLine[x].rgbtRed := (SrcLine1[t3].rgbtRed * w1 +
          SrcLine1[t3 + 1].rgbtRed * w2 +
          SrcLine2[t3].rgbtRed * w3 + SrcLine2[t3 + 1].rgbtRed * w4) shr 16;
        DstLine[x].rgbtGreen :=
          (SrcLine1[t3].rgbtGreen * w1 + SrcLine1[t3 + 1].rgbtGreen * w2 +

          SrcLine2[t3].rgbtGreen * w3 + SrcLine2[t3 + 1].rgbtGreen * w4) shr 16;
        DstLine[x].rgbtBlue := (SrcLine1[t3].rgbtBlue * w1 +
          SrcLine1[t3 + 1].rgbtBlue * w2 +
          SrcLine2[t3].rgbtBlue * w3 +
          SrcLine2[t3 + 1].rgbtBlue * w4) shr 16;
        Inc(xP, xP2);
      end; {for}
      Inc(yP, yP2);
      DstLine := pRGBArray(Integer(DstLine) + DstGap);
    end; {for}
  end; {if}
end; {SmoothResize}
 
Evo i procedure za kropovanje slike na željeni aspect radio (nešto mi je zatrebala pa sam je napisao), kome treba - eto mu je:

Kod:
procedure CropImage(bmp1, bmp2: TBitmap; ar_duz, ar_vis: Integer);
begin
  if (bmp1.Width > bmp1.Height) and ((bmp1.Width / bmp1.Height) > ar_duz / ar_vis) then
  begin
    // bmp2.width : bmp2.height = ar_duz : ar_vis
    bmp2.Height := bmp1.Height;
    bmp2.Width := (bmp2.Height * ar_duz) div ar_vis;
    bmp2.Canvas.Draw(-((bmp1.Width - bmp2.Width) div 2), 0, bmp1);
  end;
  if (bmp1.Width > bmp1.Height) and ((bmp1.Width / bmp1.Height) < ar_duz / ar_vis) then
  begin
    // bmp2.width : bmp2.height = ar_duz : ar_vis
    bmp2.Width := bmp1.Width;
    bmp2.Height := (bmp2.Width * ar_vis) div ar_duz;
    bmp2.Canvas.Draw(0, -((bmp1.Height - bmp2.Height) div 2), bmp1);
  end;
  if (bmp1.Height > bmp1.Width) and ((bmp1.Height / bmp1.Width) > ar_duz / ar_vis) then
  begin
    // bmp2.width : bmp2.height = ar_vis : ar_duz
    bmp2.Width := bmp1.Width;
    bmp2.Height := (bmp2.Width * ar_duz) div ar_vis;
    bmp2.Canvas.Draw(0, -((bmp1.Height - bmp2.Height) div 2), bmp1);
  end;
  if (bmp1.Height > bmp1.Width) and ((bmp1.Height / bmp1.Width) < ar_duz / ar_vis) then
  begin
    // bmp2.width : bmp2.height = ar_vis : ar_duz
    bmp2.Height := bmp1.Height;
    bmp2.Width := (bmp2.Height * ar_vis) div ar_duz;
    bmp2.Canvas.Draw(-((bmp1.Width - bmp2.width) div 2), 0, bmp1);
  end;
end;
 
Evo još jednog risajza, ali sada umesto da odseca delove slike koji ispadaju iz formata, ubacuje sliku čuvajući originalni aspect ratio u vaš aspect radio, a ostatak popunjava belinom (iliti "fit to paper")...

Kod:
procedure FitToPaper(bmp1, bmp2: TBitmap; ar_duz, ar_vis: Integer);
begin
  bmp2.Canvas.Brush.Color := clWhite;
  if (bmp1.Width >= bmp1.Height) and ((bmp1.Width / bmp1.Height) > ar_duz / ar_vis) then
  begin
    // bmp2.width : bmp2.height = ar_duz : ar_vis
    bmp2.Width := bmp1.Width;
    bmp2.Height := (bmp2.Width * ar_vis) div ar_duz;
    bmp2.Canvas.FillRect(Rect(0, 0, bmp2.Width, bmp2.Height));
    bmp2.Canvas.Draw(0, (bmp2.Height - bmp1.Height) div 2, bmp1);
  end;
  if (bmp1.Width >= bmp1.Height) and ((bmp1.Width / bmp1.Height) < ar_duz / ar_vis) then
  begin
    // bmp2.width : bmp2.height = ar_duz : ar_vis
    bmp2.Height := bmp1.Height;
    bmp2.Width := (bmp2.Height * ar_duz) div ar_vis;
    bmp2.Canvas.Draw((bmp2.Width - bmp1.Width) div 2, 0, bmp1);
  end;
  if (bmp1.Height > bmp1.Width) and ((bmp1.Height / bmp1.Width) > ar_duz / ar_vis) then
  begin
    // bmp2.width : bmp2.height = ar_vis : ar_duz
    bmp2.Height := bmp1.Height;
    bmp2.Width := (bmp2.Height * ar_vis) div ar_duz;
    bmp2.Canvas.Draw((bmp2.Width - bmp1.Width) div 2, 0, bmp1);
  end;
  if (bmp1.Height > bmp1.Width) and ((bmp1.Height / bmp1.Width) < ar_duz / ar_vis) then
  begin
    // bmp2.width : bmp2.height = ar_vis : ar_duz
    bmp2.Width := bmp1.Width;
    bmp2.Height := (bmp2.Width * ar_duz) div ar_vis;
    bmp2.Canvas.Draw(0, (bmp2.Height - bmp1.Height) div 2, bmp1);
  end;
end;
 
Svojevremeno sam se igrao sa ovim, ja sam direktno crtao pixele. Naime nije problem obican resize , (zubasti resize) vec treba napraviti da to lici na nesto , tipa bilinear filtering.

Ja sam to napravio i to fino radi kada treba da se smanji format ali kada s povecava onda traje mnogo dugo. Nisam se puno zaustavljao na tome ali evo mog malog primera. Tu I tamo postoje artifakti kada se u slici nalaze ciste osnovne boje i nesto mi nije u redu sa procesing thredom - puca kada s pomera mis dok radi. Nisam ispravio taj bag jer nisam imao vremena da se bakcem sa tim i bilo mi samo bitno da sam kako tako uspeo, mada znam kako da resim makar te artefakte.

PS: Probaj sa Nekom BMP fotografijom.

A evo i malo koda, Fora sa Povecanjem je sto moras da "Izmislis pixele kojih nema, to radis sa Prosecnom vrednoscu osnovnim boja (zelena, crvena i mislim plava )izmedju postojecih pixela.

Nije jednostavan algoritam znam da sam se namucio pre par godina, iskreno sada bas i nebi iz cuga moga da sve protumacim ali evo ti pa pogledaj.

A kod smanjivanja je lakse ali isto treba da "Izmisljas" pixele da bi omeksao prelaze opet gledas vertikalni i horizontalni odnos i nasnovu toga od 2 pixela pravis jedan na osnovu indexa koji predstavlja odnos izmedju nove i stare visine i sirine.
 

Prilozi

  • BilinearResize.zip
    247.8 KB · Pregleda: 31
Poslednja izmena:
Ova prva procedura i radi bilinearni risajz, ali ove moje ne... mislim, radile bi da sam u njima koristio prvu, al nekako me baš zabole :) bitno mi je da radi, a i taj moj program će se koristiti u visokim rezolucijama, tako da se nearest-pixel risajz neće preterano primećivati...

EDIT: A da, u drugoj i trećoj proceduri uopšte nema razvlačenja, tako da problema u stvari i nema :)

EDIT2: Sad sam pogledao tvoj program... rezultat je kvalitetan, ali je sporo dozlaboga :) tako da je ovaj SmoothResize trenutno bolji izbor :)
A verujem da program puca jer iz threada direktno menjaš stvari na glavnoj formi umesto da si to činio preko Synchronize metoda, jerbo ovako nije baš thread-safe...
 
Poslednja izmena:
Nazad
Vrh Dno