Konvertierung von Bild zu ASCII-Kunst


102

Prolog

Dieses Thema wird hier bei Stack Overflow von Zeit zu Zeit angezeigt, wird jedoch normalerweise entfernt, da es sich um eine schlecht geschriebene Frage handelt. Ich habe viele solcher Fragen gesehen und dann Schweigen aus dem OP (übliche niedrige Wiederholungszahl), wenn zusätzliche Informationen angefordert werden. Von Zeit zu Zeit, wenn die Eingabe für mich gut genug ist, entscheide ich mich, mit einer Antwort zu antworten und sie erhält normalerweise ein paar Up-Votes pro Tag, wenn sie aktiv ist, aber nach ein paar Wochen wird die Frage entfernt / gelöscht und alles beginnt mit dem Anfang. Also habe ich beschlossen , diese zu schreiben , Q & A , damit ich ohne Umschreiben die Antwort immer und immer wieder direkt solche Fragen verweisen kann ...

Ein weiterer Grund ist auch dieser Meta-Thread, der sich an mich richtet. Wenn Sie zusätzliche Informationen erhalten haben, können Sie diese gerne kommentieren.

Frage

Wie kann ich ein Bitmap-Bild mit C ++ in ASCII- Grafik konvertieren ?

Einige Einschränkungen:

  • Graustufenbilder
  • Verwenden von Schriftarten mit einem Abstand
  • Halten Sie es einfach (verwenden Sie nicht zu fortgeschrittenes Material für Programmierer für Anfänger)

Hier ist eine verwandte Wikipedia-Seite ASCII-Kunst (danke an @RogerRowland).

Hier ähnliches Labyrinth wie bei Fragen und Antworten zur ASCII- Kunstkonvertierung.


Können Sie anhand dieser Wiki-Seite als Referenz klären, auf welche Art von ASCII-Grafik Sie sich beziehen? Es klingt für mich wie "Bild-zu-Text-Konvertierung", eine "einfache" Suche von Graustufenpixeln zu entsprechenden Textzeichen. Ich frage mich also, ob Sie etwas anderes meinen. Es hört sich so an, als würden Sie es trotzdem selbst beantworten ...
Roger Rowland


@ RogerRowland sowohl einfach (nur Graustufenintensität basierend) als auch fortgeschrittener unter Berücksichtigung der Form der Zeichen (aber immer noch einfach genug)
Spektre

1
Während Ihre Arbeit großartig ist, würde ich mich auf jeden Fall über eine Auswahl von Samples freuen, die etwas mehr SFW sind.
kmote

@TimCastelijns Wenn Sie den Prolog lesen , dann können Sie sehen , diese diese Art von Antwort nicht das erste Mal ist , wurde aufgefordert (und die meisten Wähler von Anfang die vertraut mit einigen vorherigen Fragen im Zusammenhang damit der Rest nur entsprechend abgestimmt), Da es sich um Q & A nicht nur F Ich habe nicht zu viel Zeit mit dem Q- Teil verschwendet (was meiner Meinung nach ein Fehler ist). Ich habe der Frage einige Einschränkungen hinzugefügt, wenn Sie bessere haben. Sie können sie gerne bearbeiten.
Spektre

Antworten:


152

Es gibt weitere Ansätze für die Konvertierung von Bildern in ASCII-Grafiken, die hauptsächlich auf der Verwendung von Schriftarten mit einem Abstand basieren . Der Einfachheit halber halte ich mich nur an die Grundlagen:

Pixel- / Flächenintensität basierend (Schattierung)

Dieser Ansatz behandelt jedes Pixel eines Pixelbereichs als einen einzelnen Punkt. Die Idee ist, die durchschnittliche Graustufenintensität dieses Punkts zu berechnen und ihn dann durch ein Zeichen zu ersetzen, dessen Intensität nahe genug an der berechneten liegt. Dafür benötigen wir eine Liste verwendbarer Zeichen mit jeweils vorberechneter Intensität. Nennen wir es einen Charakter map. Es gibt zwei Möglichkeiten, um schneller auszuwählen, welcher Charakter für welche Intensität am besten geeignet ist:

  1. Linear verteilte Intensitätszeichenkarte

    Wir verwenden also nur Zeichen, die mit demselben Schritt einen Intensitätsunterschied aufweisen. Mit anderen Worten, wenn sortiert aufsteigend dann:

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

    Auch wenn unser Charakter mapsortiert ist, können wir den Charakter direkt aus der Intensität berechnen (keine Suche erforderlich)

     character = map[intensity_of(dot)/constant];
  2. Beliebig verteilte Intensitätszeichenkarte

    Wir haben also eine Reihe verwendbarer Zeichen und deren Intensität. Wir müssen die Intensität finden, die dem am nächsten kommt. intensity_of(dot)Wenn wir also die sortieren map[], können wir die binäre Suche verwenden, andernfalls benötigen wir eine O(n)Such-Mindestabstandsschleife oder ein O(1)Wörterbuch. Manchmal kann der Charakter der Einfachheit halber map[]als linear verteilt behandelt werden, was zu einer leichten Gamma-Verzerrung führt, die im Ergebnis normalerweise nicht sichtbar ist, es sei denn, Sie wissen, wonach Sie suchen müssen.

Die intensitätsbasierte Konvertierung eignet sich auch hervorragend für Graustufenbilder (nicht nur für Schwarzweißbilder). Wenn Sie den Punkt als einzelnes Pixel auswählen, wird das Ergebnis groß (ein Pixel -> einzelnes Zeichen). Bei größeren Bildern wird stattdessen ein Bereich (Multiplikation der Schriftgröße) ausgewählt, um das Seitenverhältnis beizubehalten und nicht zu stark zu vergrößern.

Wie es geht:

  1. Gleichmäßig teilen das Bild in (Graustufen) oder Pixel (rechteckig) Bereiche dot s
  2. Berechnen Sie die Intensität jedes Pixels / Bereichs
  3. Ersetzen Sie es durch ein Zeichen aus der Zeichenkarte mit der nächsten Intensität

Als Zeichen können mapSie beliebige Zeichen verwenden. Das Ergebnis wird jedoch besser, wenn die Pixel des Zeichens gleichmäßig über den Zeichenbereich verteilt sind. Für den Anfang können Sie verwenden:

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

sortiert absteigend und geben vor, linear verteilt zu sein.

Wenn also die Intensität des Pixels / der Fläche ist, i = <0-255>ist das Ersatzzeichen

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

Wenn i==0dann das Pixel / der Bereich schwarz ist, wenn i==127dann das Pixel / der Bereich grau ist und wenn i==255dann das Pixel / der Bereich weiß ist. Sie können mit verschiedenen Charakteren im Inneren experimentieren map[]...

Hier ist ein altes Beispiel von mir in C ++ und VCL:

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;

Sie müssen VCL-Inhalte ersetzen / ignorieren, es sei denn, Sie verwenden die Borland / Embarcadero- Umgebung.

  • mm_log ist das Memo, in dem der Text ausgegeben wird
  • bmp ist die Eingabe-Bitmap
  • AnsiStringist eine VCL-Zeichenfolge, die von 1 indiziert ist, nicht von 0 als char*!!!

Dies ist das Ergebnis: Ein Beispielbild mit leicht NSFW-Intensität

Auf der linken Seite befindet sich die ASCII-Grafikausgabe (Schriftgröße 5 Pixel), und auf der rechten Seite wird das Eingabebild einige Male gezoomt . Wie Sie sehen können, ist die Ausgabe ein größeres Pixel -> Zeichen. Wenn Sie größere Bereiche anstelle von Pixeln verwenden, ist der Zoom kleiner, aber die Ausgabe ist natürlich optisch weniger ansprechend. Dieser Ansatz ist sehr einfach und schnell zu codieren / zu verarbeiten.

Wenn Sie erweiterte Dinge hinzufügen wie:

  • automatisierte Kartenberechnungen
  • automatische Auswahl der Pixel- / Flächengröße
  • Seitenverhältniskorrekturen

Dann können Sie komplexere Bilder mit besseren Ergebnissen verarbeiten:

Hier ist das Ergebnis in einem Verhältnis von 1: 1 (Zoom, um die Zeichen zu sehen):

Beispiel für fortgeschrittene Intensität

Natürlich verlieren Sie bei der Flächenprobenahme die kleinen Details. Dies ist ein Bild mit der gleichen Größe wie das erste Beispiel, das mit Bereichen abgetastet wurde:

Etwas fortgeschrittenes Beispielbild mit NSFW-Intensität

Wie Sie sehen können, ist dies besser für größere Bilder geeignet.

Zeichenanpassung (Hybrid zwischen Schattierung und fester ASCII-Grafik)

Dieser Ansatz versucht, den Bereich (keine einzelnen Pixelpunkte mehr) durch Zeichen mit ähnlicher Intensität und Form zu ersetzen. Dies führt zu besseren Ergebnissen, selbst bei größeren Schriftarten, die im Vergleich zum vorherigen Ansatz verwendet werden. Andererseits ist dieser Ansatz natürlich etwas langsamer. Es gibt mehr Möglichkeiten, dies zu tun, aber die Hauptidee besteht darin, den Unterschied (Abstand) zwischen dem Bildbereich ( dot) und dem gerenderten Zeichen zu berechnen . Sie können mit einer naiven Summe der absoluten Differenz zwischen Pixeln beginnen, aber das führt zu nicht sehr guten Ergebnissen, da selbst eine Verschiebung um ein Pixel den Abstand groß macht. Stattdessen können Sie Korrelationen oder andere Metriken verwenden. Der Gesamtalgorithmus ist fast der gleiche wie beim vorherigen Ansatz:

  1. So gleichmäßig um das Bild zu unterteilen (Grauskala) rechteckige Bereiche dot ‚s

    Idealerweise mit demselben Seitenverhältnis wie gerenderte Schriftzeichen (das Seitenverhältnis bleibt erhalten. Vergessen Sie nicht, dass sich Zeichen auf der x-Achse normalerweise etwas überlappen.)

  2. Berechnen Sie die Intensität jedes Bereichs ( dot)

  3. Ersetzen Sie es durch ein Zeichen aus dem Zeichen mapmit der nächsten Intensität / Form

Wie können wir den Abstand zwischen einem Zeichen und einem Punkt berechnen? Das ist der schwierigste Teil dieses Ansatzes. Beim Experimentieren entwickle ich diesen Kompromiss zwischen Geschwindigkeit, Qualität und Einfachheit:

  1. Teilen Sie den Charakterbereich in Zonen

    Zonen

    • Berechnen Sie aus Ihrem Konvertierungsalphabet ( map) eine separate Intensität für die linke, rechte, obere, untere und mittlere Zone jedes Zeichens .
    • Normalisieren Sie alle Intensitäten, damit sie unabhängig von der Flächengröße sind i=(i*256)/(xs*ys).
  2. Verarbeiten Sie das Quellbild in rechteckigen Bereichen

    • (mit dem gleichen Seitenverhältnis wie die Zielschriftart)
    • Berechnen Sie für jeden Bereich die Intensität auf die gleiche Weise wie in Punkt 1
    • Finden Sie die engste Übereinstimmung aus den Intensitäten im Konvertierungsalphabet
    • Geben Sie das angepasste Zeichen aus

Dies ist das Ergebnis für eine Schriftgröße von 7 Pixel

Beispiel für die Zeichenanpassung

Wie Sie sehen können, ist die Ausgabe auch bei einer größeren verwendeten Schriftgröße optisch ansprechend (das vorherige Ansatzbeispiel war eine Schriftgröße von 5 Pixel). Die Ausgabe hat ungefähr die gleiche Größe wie das Eingabebild (kein Zoom). Die besseren Ergebnisse werden erzielt, weil die Zeichen nicht nur durch die Intensität, sondern auch durch die Gesamtform näher am Originalbild sind. Daher können Sie größere Schriftarten verwenden und dennoch Details beibehalten (bis zu einem gewissen Punkt natürlich).

Hier ist der vollständige Code für die VCL-basierte Konvertierungsanwendung:

//---------------------------------------------------------------------------
#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();
}

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

Es ist einfach eine Formularanwendung ( Form1) mit einer einzelnen TMemo mm_txtdarin. Es lädt ein Bild "pic.bmp"und wählt dann entsprechend der Auflösung aus, welcher Ansatz zum Konvertieren in Text verwendet werden soll, "pic.txt"der in einem Memo gespeichert und zur Visualisierung gesendet wird.

Wenn Sie keine VCL haben, ignorieren Sie das VCL-Material und ersetzen Sie es AnsiStringdurch einen beliebigen Zeichenfolgentyp sowie Graphics::TBitmapdurch eine Bitmap oder Bildklasse, die Ihnen mit Pixelzugriffsfunktion zur Verfügung steht.

Ein sehr wichtiger Hinweis ist, dass hierfür die Einstellungen von verwendet mm_txt->Fontwerden. Stellen Sie daher Folgendes sicher:

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

Damit dies ordnungsgemäß funktioniert, wird die Schriftart sonst nicht als Mono behandelt. Das Mausrad ändert lediglich die Schriftgröße nach oben / unten, um Ergebnisse für verschiedene Schriftgrößen anzuzeigen.

[Anmerkungen]

  • Sehen Visualisierung von Wortporträts
  • Verwenden Sie eine Sprache mit Bitmap- / Dateizugriffs- und Textausgabefunktionen
  • Ich empfehle dringend, mit dem ersten Ansatz zu beginnen, da dieser sehr einfach und unkompliziert ist, und erst dann zum zweiten überzugehen (was als Modifikation des ersten erfolgen kann, sodass der größte Teil des Codes unverändert bleibt).
  • Es ist eine gute Idee, mit invertierter Intensität zu berechnen (schwarze Pixel sind der Maximalwert), da die Standardtextvorschau auf einem weißen Hintergrund angezeigt wird, was zu viel besseren Ergebnissen führt.
  • Sie können mit Größe, Anzahl und Layout der Unterteilungszonen experimentieren oder 3x3stattdessen ein Raster wie verwenden.

Vergleich

Schließlich ist hier ein Vergleich zwischen den beiden Ansätzen auf dem gleichen Eingang:

Vergleich

Die mit grünen Punkten markierten Bilder werden mit Ansatz Nr. 2 und die roten mit Nr. 1 mit einer Schriftgröße von sechs Pixeln erstellt. Wie Sie auf dem Bild der Glühbirne sehen können, ist der formempfindliche Ansatz viel besser (selbst wenn die Nummer 1 ist bei einem 2-fach gezoomten Quellbild erfolgt).

Coole Anwendung

Beim Lesen der heutigen neuen Fragen kam mir eine Idee für eine coole Anwendung, die einen ausgewählten Bereich des Desktops erfasst und kontinuierlich dem ASCIIart-Konverter zuführt und das Ergebnis anzeigt . Nach einer Stunde Codierung ist es geschafft und ich bin so zufrieden mit dem Ergebnis, dass ich es einfach hier hinzufügen muss.

OK, die Anwendung besteht nur aus zwei Fenstern. Das erste Hauptfenster ist im Grunde mein altes Konverterfenster ohne Bildauswahl und Vorschau (alles oben Genannte ist drin). Es hat nur die ASCII-Vorschau- und Konvertierungseinstellungen. Das zweite Fenster ist ein leeres Formular mit einer transparenten Innenseite für die Auswahl des Greifbereichs (keinerlei Funktionalität).

Jetzt greife ich auf einem Timer einfach zum ausgewählten Bereich im Auswahlformular, übergebe ihn der Konvertierung und zeige eine Vorschau des ASCIIart .

Sie schließen also einen Bereich, den Sie konvertieren möchten, in das Auswahlfenster ein und zeigen das Ergebnis im Hauptfenster an. Es kann ein Spiel, ein Zuschauer usw. sein. Es sieht so aus:

Beispiel für einen ASCIIart-Grabber

Jetzt kann ich zum Spaß sogar Videos in ASCIIart ansehen . Einige sind wirklich nett :).

Hände

Wenn Sie versuchen möchten, dies in GLSL zu implementieren , sehen Sie sich Folgendes an:


30
Du hast hier einen unglaublichen Job gemacht! Vielen Dank! Und ich liebe die ASCII-Zensur!
Ander Biguri

1
Ein Verbesserungsvorschlag: Erarbeiten Sie Richtungsableitungen, nicht nur Intensität.
Yakk - Adam Nevraumont

1
@ Yakk möchten Sie näher darauf eingehen?
Tariksbl

2
@tarik passt entweder nicht nur zur Intensität, sondern auch zu Ableitungen: oder Bandpass-Verbesserungskanten. Grundsätzlich ist Intensität nicht das einzige, was Menschen sehen: Sie sehen Gradanten und Kanten.
Yakk - Adam Nevraumont

1
@ Yakk die Zonenunterteilung macht so etwas indirekt. Vielleicht wäre es sogar noch besser, Zeichen als 3x3Zonen zu behandeln und die DCTs zu vergleichen, aber das würde die Leistung meiner Meinung nach stark verringern.
Spektre
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.