ASCII sanat dönüşümüne görüntü


103

Önsöz

Bu konu zaman zaman Stack Overflow'da ortaya çıkar, ancak genellikle kötü yazılmış bir soru olduğu için kaldırılır. Bu tür birçok soru gördüm ve ek bilgi talep edildiğinde OP'den (normal düşük temsilci) sessiz kaldım . Zaman zaman girdi benim için yeterince iyiyse, bir cevapla yanıt vermeye karar veriyorum ve genellikle aktifken günde birkaç artı oy alıyor, ancak birkaç hafta sonra soru kaldırılıyor / siliniyor ve hepsi başlangıç. Bu yüzden bu Soru-Cevap'ı yazmaya karar verdim, böylece yanıtı defalarca yeniden yazmadan bu tür sorulara doğrudan başvurabilirim…

Başka bir neden de bu meta ileti dizisinin beni hedeflemesidir, bu nedenle ek girdiniz varsa yorum yapmaktan çekinmeyin.

Soru

C ++ kullanarak bir bitmap görüntüsünü ASCII sanatına nasıl dönüştürebilirim ?

Bazı kısıtlamalar:

  • gri tonlamalı görüntüler
  • tek aralıklı yazı tiplerini kullanma
  • basit tutmak (başlangıç ​​seviyesindeki programcılar için çok gelişmiş şeyler kullanmamak)

İşte ilgili Wikipedia sayfası ASCII sanatı (@RogerRowland sayesinde).

İşte ASCII Sanat dönüştürme Soru- Cevap'a benzer labirent .


Bu wiki sayfasını referans olarak kullanarak , hangi ASCII sanatına atıfta bulunduğunuzu açıklayabilir misiniz? Bana gri tonlamalı piksellerden karşılık gelen metin karakterine "basit" bir arama olan "Görüntüden metne dönüştürme" gibi geliyor, bu yüzden farklı bir şey mi kastettiğinizi merak ediyorum. Görünüşe göre yine de kendin cevaplayacaksın .....
Roger Rowland


@RogerRowland hem basit (yalnızca gri tonlama yoğunluğuna dayalı) hem de karakterlerin şeklini de hesaba katarak daha gelişmiş (ama yine de yeterince basit)
Spektre

1
Çalışmanız harika olsa da, biraz daha SFW olan örneklerin bir seçimini kesinlikle takdir ediyorum.
kmote

@TimCastelijns Prologu okursanız, bu tür bir yanıtın ilk kez talep edilmediğini görebilirsiniz (ve çoğu seçmen, daha önceki birkaç soruya aşina olduğu ve geri kalanı buna göre oy vermiştir), çünkü bu sadece Soru-Cevap değil S Q kısmı ile çok fazla zaman kaybetmedim (ki bu benim tarafımdan bir hata), soruya birkaç kısıtlama ekledim, eğer daha iyi olanları varsa, düzenlemekten çekinmeyin.
Spektre

Yanıtlar:


153

Görüntüden ASCII sanat dönüştürmesine yönelik daha çok yaklaşım vardır ve bunlar çoğunlukla tek aralıklı yazı tiplerini kullanır . Basit olması için sadece temellere bağlıyım:

Piksel / alan yoğunluğuna dayalı (gölgeleme)

Bu yaklaşım, bir piksel alanının her pikselini tek bir nokta olarak ele alır. Buradaki fikir, bu noktanın ortalama gri tonlama yoğunluğunu hesaplamak ve ardından onu hesaplanana yeterince yakın yoğunluğa sahip bir karakterle değiştirmektir. Bunun için, her biri önceden hesaplanmış yoğunluğa sahip bazı kullanılabilir karakterlerin listesine ihtiyacımız var. Buna karakter diyelim map. Hangi karakterin hangi yoğunluk için en iyi olduğunu daha hızlı seçmenin iki yolu vardır:

  1. Doğrusal olarak dağıtılmış yoğunluk karakter haritası

    Bu yüzden sadece aynı adımda yoğunluk farkı olan karakterleri kullanıyoruz. Başka bir deyişle, artan düzende sıralandığında:

     intensity_of(map[i])=intensity_of(map[i-1])+constant;

    Ayrıca karakterimiz mapsıralandığında, karakteri doğrudan yoğunluktan hesaplayabiliriz (arama gerekmez)

     character = map[intensity_of(dot)/constant];
  2. Keyfi dağıtılmış yoğunluk karakter haritası

    Yani bir dizi kullanılabilir karakterimiz ve bunların yoğunlukları var. So'ya en yakın yoğunluğu bulmalıyız, intensity_of(dot)eğer 'yi sıralarsak, map[]ikili aramayı kullanabiliriz, aksi takdirde bir O(n)arama minimum mesafe döngüsüne veya O(1)sözlüğe ihtiyacımız var. Bazen basitlik için, karakter map[]doğrusal olarak dağıtılmış olarak ele alınabilir ve neye bakacağınızı bilmediğiniz sürece genellikle sonuçta görünmeyen hafif bir gama bozulmasına neden olabilir.

Yoğunluğa dayalı dönüştürme, gri ölçekli görüntüler için de harikadır (yalnızca siyah beyaz değil). Noktayı tek bir piksel olarak seçerseniz, sonuç büyür (bir piksel -> tek karakter), bu nedenle daha büyük görüntüler için en boy oranını korumak ve çok fazla büyütmemek için bir alan (yazı tipi boyutunun çarpımı) seçilir.

Nasıl yapılır:

  1. Görüntüyü (gri ölçekli) piksellere veya (dikdörtgen) alanlara eşit olarak bölün nokta s
  2. Her pikselin / alanın yoğunluğunu hesaplayın
  3. En yakın yoğunluğa sahip karakter haritasındaki karakterle değiştirin

Karakter olarak mapherhangi bir karakter kullanabilirsiniz, ancak karakterin karakter alanı boyunca eşit olarak dağılmış pikselleri varsa sonuç daha iyi olur. Yeni başlayanlar için şunları kullanabilirsiniz:

  • char map[10]=" .,:;ox%#@";

azalan sıralanır ve doğrusal olarak dağıtılmış gibi görünür.

Öyleyse, piksel / alan yoğunluğu ise i = <0-255>, ikame karakter olacaktır

  • map[(255-i)*10/256];

Eğer i==0o zaman piksel / alan siyah i==127sonra piksel / alan gri ise ve i==255daha sonra piksel / alan beyazdır. İçinde farklı karakterleri deneyebilirsiniz map[]...

İşte C ++ ve VCL'deki eski bir örnek:

AnsiString m = " .,:;ox%#@";
Graphics::TBitmap *bmp = new Graphics::TBitmap;
bmp->LoadFromFile("pic.bmp");
bmp->HandleType = bmDIB;
bmp->PixelFormat = pf24bit;

int x, y, i, c, l;
BYTE *p;
AnsiString s, endl;
endl = char(13); endl += char(10);
l = m.Length();
s ="";
for (y=0; y<bmp->Height; y++)
{
    p = (BYTE*)bmp->ScanLine[y];
    for (x=0; x<bmp->Width; x++)
    {
        i  = p[x+x+x+0];
        i += p[x+x+x+1];
        i += p[x+x+x+2];
        i = (i*l)/768;
        s += m[l-i];
    }
    s += endl;
}
mm_log->Lines->Text = s;
mm_log->Lines->SaveToFile("pic.txt");
delete bmp;

Borland / Embarcadero ortamını kullanmadığınız sürece VCL öğelerini değiştirmeniz / yok saymanız gerekir .

  • mm_log metnin çıktısının alındığı not
  • bmp girdi bit eşlemi
  • AnsiString1'den indekslenmiş bir VCL türü dizedir, 0'dan değil char*!!!

Sonuç: Biraz NSFW yoğunluğu örnek resmi

Solda ASCII sanat çıktısı (yazı tipi boyutu 5 piksel) ve sağda giriş görüntüsü birkaç kez yakınlaştırılmış . Gördüğünüz gibi, çıktı daha büyük piksel -> karakterdir. Piksel yerine daha geniş alanlar kullanırsanız, yakınlaştırma daha küçük olur, ancak elbette çıktı görsel olarak daha az hoştur. Bu yaklaşım, kodlanması / işlenmesi çok kolay ve hızlıdır.

Aşağıdakiler gibi daha gelişmiş şeyler eklediğinizde:

  • otomatik harita hesaplamaları
  • otomatik piksel / alan boyutu seçimi
  • en boy oranı düzeltmeleri

Daha sonra daha karmaşık görüntüleri daha iyi sonuçlarla işleyebilirsiniz:

Sonuç 1: 1 oranındadır (karakterleri görmek için yakınlaştırın):

Gelişmiş yoğunluk örneği

Elbette, alan örneklemesi için küçük ayrıntıları kaybedersiniz. Bu, alanlarla örneklenen ilk örnekle aynı boyutta bir görüntüdür:

Biraz NSFW yoğunluğu gelişmiş örnek resim

Gördüğünüz gibi, bu daha büyük görüntüler için daha uygun.

Karakter uydurma (gölgeleme ve katı ASCII sanatı arasında karma)

Bu yaklaşım, alanı (artık tek piksel noktası yok) benzer yoğunluk ve şekle sahip karakterle değiştirmeye çalışır. Bu, önceki yaklaşımla karşılaştırıldığında daha büyük yazı tiplerinde bile daha iyi sonuçlara yol açar. Öte yandan, bu yaklaşım elbette biraz daha yavaştır. Bunu yapmanın daha fazla yolu vardır, ancak ana fikir, görüntü alanı ( dot) ile oluşturulan karakter arasındaki farkı (mesafeyi) hesaplamaktır . Pikseller arasındaki mutlak farkın saf toplamıyla başlayabilirsiniz, ancak bu çok iyi sonuçlara yol açmayacaktır çünkü tek piksellik bir kayma bile mesafeyi büyütecektir. Bunun yerine korelasyon veya farklı ölçümler kullanabilirsiniz. Genel algoritma, önceki yaklaşımla neredeyse aynıdır:

  1. Yani eşit görüntüyü bölmek (gri ölçekli) dikdörtgen alanlar dot 'ler

    ideal olarak, oluşturulmuş yazı tipi karakterleriyle aynı en boy oranıyla (en boy oranını koruyacaktır. Karakterlerin genellikle x ekseninde bir bit üst üste geldiğini unutmayın)

  2. Her alanın yoğunluğunu hesaplayın ( dot)

  3. mapEn yakın yoğunluk / şekle sahip karakterden bir karakterle değiştirin

Bir karakter ve bir nokta arasındaki mesafeyi nasıl hesaplayabiliriz? Bu yaklaşımın en zor kısmı budur. Deney yaparken hız, kalite ve basitlik arasındaki bu uzlaşmayı geliştiriyorum:

  1. Karakter alanını bölgelere böl

    Bölgeler

    • Dönüşüm alfabenizden ( map) her karakterin sol, sağ, yukarı, aşağı ve orta bölgesi için ayrı bir yoğunluk hesaplayın .
    • Tüm yoğunlukları normalleştirin, böylece alan boyutundan bağımsız olurlar i=(i*256)/(xs*ys).
  2. Kaynak görüntüyü dikdörtgen alanlarda işleyin

    • (hedef yazı tipiyle aynı en boy oranına sahip)
    • Her alan için yoğunluğu 1 numaralı madde işaretiyle aynı şekilde hesaplayın
    • Dönüşüm alfabesindeki yoğunluklardan en yakın eşleşmeyi bulun
    • Yerleştirilen karakteri çıktılar

Bu, yazı tipi boyutu = 7 piksel sonucudur

Karakter uydurma örneği

Gördüğünüz gibi, daha büyük bir yazı tipi boyutu kullanıldığında bile çıktı görsel olarak hoştur (önceki yaklaşım örneği 5 piksel yazı tipi boyutundaydı). Çıktı, giriş görüntüsüyle kabaca aynı boyuttadır (yakınlaştırma yok). Daha iyi sonuçlar elde edilir, çünkü karakterler yalnızca yoğunluk açısından değil, aynı zamanda genel şekil açısından da orijinal görüntüye daha yakındır ve bu nedenle daha büyük yazı tiplerini kullanabilir ve yine de ayrıntıları koruyabilirsiniz (tabii ki bir noktaya kadar).

İşte VCL tabanlı dönüştürme uygulaması için tam kod:

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;
Graphics::TBitmap *bmp=new Graphics::TBitmap;
//---------------------------------------------------------------------------


class intensity
{
public:
    char c;                    // Character
    int il, ir, iu ,id, ic;    // Intensity of part: left,right,up,down,center
    intensity() { c=0; reset(); }
    void reset() { il=0; ir=0; iu=0; id=0; ic=0; }

    void compute(DWORD **p,int xs,int ys,int xx,int yy) // p source image, (xs,ys) area size, (xx,yy) area position
    {
        int x0 = xs>>2, y0 = ys>>2;
        int x1 = xs-x0, y1 = ys-y0;
        int x, y, i;
        reset();
        for (y=0; y<ys; y++)
            for (x=0; x<xs; x++)
            {
                i = (p[yy+y][xx+x] & 255);
                if (x<=x0) il+=i;
                if (x>=x1) ir+=i;
                if (y<=x0) iu+=i;
                if (y>=x1) id+=i;

                if ((x>=x0) && (x<=x1) &&
                    (y>=y0) && (y<=y1))

                    ic+=i;
        }

        // Normalize
        i = xs*ys;
        il = (il << 8)/i;
        ir = (ir << 8)/i;
        iu = (iu << 8)/i;
        id = (id << 8)/i;
        ic = (ic << 8)/i;
        }
    };


//---------------------------------------------------------------------------
AnsiString bmp2txt_big(Graphics::TBitmap *bmp,TFont *font) // Character  sized areas
{
    int i, i0, d, d0;
    int xs, ys, xf, yf, x, xx, y, yy;
    DWORD **p = NULL,**q = NULL;    // Bitmap direct pixel access
    Graphics::TBitmap *tmp;        // Temporary bitmap for single character
    AnsiString txt = "";            // Output ASCII art text
    AnsiString eol = "\r\n";        // End of line sequence
    intensity map[97];            // Character map
    intensity gfx;

    // Input image size
    xs = bmp->Width;
    ys = bmp->Height;

    // Output font size
    xf = font->Size;   if (xf<0) xf =- xf;
    yf = font->Height; if (yf<0) yf =- yf;

    for (;;) // Loop to simplify the dynamic allocation error handling
    {
        // Allocate and initialise buffers
        tmp = new Graphics::TBitmap;
        if (tmp==NULL)
            break;

        // Allow 32 bit pixel access as DWORD/int pointer
        tmp->HandleType = bmDIB;    bmp->HandleType = bmDIB;
        tmp->PixelFormat = pf32bit; bmp->PixelFormat = pf32bit;

        // Copy target font properties to tmp
        tmp->Canvas->Font->Assign(font);
        tmp->SetSize(xf, yf);
        tmp->Canvas->Font ->Color = clBlack;
        tmp->Canvas->Pen  ->Color = clWhite;
        tmp->Canvas->Brush->Color = clWhite;
        xf = tmp->Width;
        yf = tmp->Height;

        // Direct pixel access to bitmaps
        p  = new DWORD*[ys];
        if (p  == NULL) break;
        for (y=0; y<ys; y++)
            p[y] = (DWORD*)bmp->ScanLine[y];

        q  = new DWORD*[yf];
        if (q  == NULL) break;
        for (y=0; y<yf; y++)
            q[y] = (DWORD*)tmp->ScanLine[y];

        // Create character map
        for (x=0, d=32; d<128; d++, x++)
        {
            map[x].c = char(DWORD(d));
            // Clear tmp
            tmp->Canvas->FillRect(TRect(0, 0, xf, yf));
            // Render tested character to tmp
            tmp->Canvas->TextOutA(0, 0, map[x].c);

            // Compute intensity
            map[x].compute(q, xf, yf, 0, 0);
        }

        map[x].c = 0;

        // Loop through the image by zoomed character size step
        xf -= xf/3; // Characters are usually overlapping by 1/3
        xs -= xs % xf;
        ys -= ys % yf;
        for (y=0; y<ys; y+=yf, txt += eol)
            for (x=0; x<xs; x+=xf)
            {
                // Compute intensity
                gfx.compute(p, xf, yf, x, y);

                // Find the closest match in map[]
                i0 = 0; d0 = -1;
                for (i=0; map[i].c; i++)
                {
                    d = abs(map[i].il-gfx.il) +
                        abs(map[i].ir-gfx.ir) +
                        abs(map[i].iu-gfx.iu) +
                        abs(map[i].id-gfx.id) +
                        abs(map[i].ic-gfx.ic);

                    if ((d0<0)||(d0>d)) {
                        d0=d; i0=i;
                    }
                }
                // Add fitted character to output
                txt += map[i0].c;
            }
        break;
    }

    // Free buffers
    if (tmp) delete tmp;
    if (p  ) delete[] p;
    return txt;
}


//---------------------------------------------------------------------------
AnsiString bmp2txt_small(Graphics::TBitmap *bmp)    // pixel sized areas
{
    AnsiString m = " `'.,:;i+o*%&$#@"; // Constant character map
    int x, y, i, c, l;
    BYTE *p;
    AnsiString txt = "", eol = "\r\n";
    l = m.Length();
    bmp->HandleType = bmDIB;
    bmp->PixelFormat = pf32bit;
    for (y=0; y<bmp->Height; y++)
    {
        p = (BYTE*)bmp->ScanLine[y];
        for (x=0; x<bmp->Width; x++)
        {
            i  = p[(x<<2)+0];
            i += p[(x<<2)+1];
            i += p[(x<<2)+2];
            i  = (i*l)/768;
            txt += m[l-i];
        }
        txt += eol;
    }
    return txt;
}


//---------------------------------------------------------------------------
void update()
{
    int x0, x1, y0, y1, i, l;
    x0 = bmp->Width;
    y0 = bmp->Height;
    if ((x0<64)||(y0<64)) Form1->mm_txt->Text = bmp2txt_small(bmp);
     else                  Form1->mm_txt->Text = bmp2txt_big  (bmp, Form1->mm_txt->Font);
    Form1->mm_txt->Lines->SaveToFile("pic.txt");
    for (x1 = 0, i = 1, l = Form1->mm_txt->Text.Length();i<=l;i++) if (Form1->mm_txt->Text[i] == 13) { x1 = i-1; break; }
    for (y1=0, i=1, l=Form1->mm_txt->Text.Length();i <= l; i++) if (Form1->mm_txt->Text[i] == 13) y1++;
    x1 *= abs(Form1->mm_txt->Font->Size);
    y1 *= abs(Form1->mm_txt->Font->Height);
    if (y0<y1) y0 = y1; x0 += x1 + 48;
    Form1->ClientWidth = x0;
    Form1->ClientHeight = y0;
    Form1->Caption = AnsiString().sprintf("Picture -> Text (Font %ix%i)", abs(Form1->mm_txt->Font->Size), abs(Form1->mm_txt->Font->Height));
}


//---------------------------------------------------------------------------
void draw()
{
    Form1->ptb_gfx->Canvas->Draw(0, 0, bmp);
}


//---------------------------------------------------------------------------
void load(AnsiString name)
{
    bmp->LoadFromFile(name);
    bmp->HandleType = bmDIB;
    bmp->PixelFormat = pf32bit;
    Form1->ptb_gfx->Width = bmp->Width;
    Form1->ClientHeight = bmp->Height;
    Form1->ClientWidth = (bmp->Width << 1) + 32;
}


//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
    load("pic.bmp");
    update();
}


//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
    delete bmp;
}


//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
    draw();
}


//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheel(TObject *Sender, TShiftState Shift, int WheelDelta, TPoint &MousePos, bool &Handled)
{
    int s = abs(mm_txt->Font->Size);
    if (WheelDelta<0) s--;
    if (WheelDelta>0) s++;
    mm_txt->Font->Size = s;
    update();
}

//---------------------------------------------------------------------------

İçinde Form1tek TMemo mm_txtbulunan bir form uygulamasıdır ( ) . Bir görüntü yükler "pic.bmp"ve ardından çözünürlüğe göre, "pic.txt"görselleştirmek için nota kaydedilen ve nota gönderilen metne dönüştürmek için hangi yaklaşımın kullanılacağını seçer .

VCL'si olmayanlar için, VCL öğelerini yok sayın ve AnsiStringsahip olduğunuz herhangi bir dize türüyle ve ayrıca Graphics::TBitmappiksel erişim özelliğine sahip elinizde bulunan herhangi bir bitmap veya görüntü sınıfıyla değiştirin.

Çok önemli bir not, bunun ayarlarını kullanmasıdır mm_txt->Font, bu nedenle şunları ayarladığınızdan emin olun:

  • Font->Pitch = fpFixed
  • Font->Charset = OEM_CHARSET
  • Font->Name = "System"

bunun düzgün çalışmasını sağlamak için, aksi takdirde yazı tipi tek aralıklı olarak ele alınmayacaktır. Fare tekerleği, sonuçları farklı yazı tipi boyutlarında görmek için yazı tipi boyutunu yukarı / aşağı değiştirir.

[Notlar]

  • Word Portreleri görselleştirmesine bakın
  • Bitmap / dosya erişimi ve metin çıktı yeteneklerine sahip bir dil kullanın
  • Çok kolay anlaşılır ve basit olduğu için ilk yaklaşımla başlamanızı ve ancak o zaman ikinciye geçmenizi şiddetle tavsiye ederim (bu, ilkinin değiştirilmesi olarak yapılabilir, böylece kodun çoğu olduğu gibi kalır)
  • Tersine çevrilmiş yoğunlukta (siyah pikseller maksimum değerdir) hesaplama yapmak iyi bir fikirdir çünkü standart metin önizlemesi beyaz bir arka plan üzerindedir ve bu nedenle çok daha iyi sonuçlar sağlar.
  • Alt bölüm bölgelerinin boyutu, sayısı ve düzenini deneyebilir veya 3x3bunun yerine bazı ızgaraları kullanabilirsiniz .

Karşılaştırma

Son olarak, aynı girdiye ilişkin iki yaklaşım arasında bir karşılaştırma:

Karşılaştırma

Yeşil nokta işaretli resimler , tümü altı piksel yazı tipi boyutunda olmak üzere , yaklaşım # 2 ve kırmızı olanlar # 1 ile yapılır . Ampul görüntüsünde görebileceğiniz gibi, şekle duyarlı yaklaşım çok daha iyidir ( # 1 2x yakınlaştırılmış kaynak görüntüde yapılsa bile ).

Harika uygulama

Bugünün yeni sorularını okurken, masaüstünün seçilen bir bölgesini yakalayan ve onu sürekli olarak ASCIIart dönüştürücüsüne besleyen ve sonucu görüntüleyen harika bir uygulama fikrimi aldım . Bir saatlik kodlamadan sonra, bitti ve sonuçtan o kadar memnunum ki, onu buraya eklemem gerekiyor.

Tamam uygulama sadece iki pencereden oluşuyor. İlk ana pencere, temelde görüntü seçimi ve önizlemesi olmayan eski dönüştürücü penceremdir (yukarıdaki tüm şeyler içindedir). Yalnızca ASCII önizleme ve dönüştürme ayarlarına sahiptir. İkinci pencere, kapma alanı seçimi için içi şeffaf olan boş bir formdur (herhangi bir işlevsellik yoktur).

Şimdi bir zamanlayıcıda, sadece seçilen alanı seçim formundan alıyorum , dönüşüme geçiriyorum ve ASCIIart'ı önizliyorum .

Böylece dönüştürmek istediğiniz alanı seçim penceresi ile çevreliyor ve sonucu ana pencerede görüntüleyebiliyorsunuz. Bu bir oyun, izleyici vb. Olabilir. Şuna benzer:

ASCIIart yakalayıcı örneği

Artık eğlenmek için ASCIIart'taki videoları bile izleyebiliyorum . Bazıları gerçekten güzel :).

Eller

Bunu GLSL'de uygulamayı denemek istiyorsanız , şuna bir göz atın:


30
Burada inanılmaz bir iş başardın! Teşekkürler! Ve ASCII sansürünü seviyorum!
Ander Biguri

1
İyileştirme için bir öneri: sadece yoğunluğu değil, yönlü türevleri hesaplayın.
Yakk - Adam Nevraumont

1
@Yakk detaylandırmak ister misiniz?
tariksbl

2
@tarik ya yalnızca yoğunlukta değil, türevlerle de eşleşir: veya bant geçişi kenarları geliştirir. Temelde insanların gördüğü tek şey yoğunluk değildir: geçişleri ve kenarları görürler.
Yakk - Adam Nevraumont

1
@Yakk the zones subdivision böyle bir şeyi dolaylı olarak yapar. Karakterleri 3x3bölgeler olarak ele alıp DCT'leri karşılaştırmak daha da iyi olabilirdi , ancak bu bence performansı çok düşürür.
Spektre
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.