Chuyển đổi hình ảnh sang nghệ thuật ASCII


102

Lời mở đầu

Chủ đề này thỉnh thoảng xuất hiện ở đây trên Stack Overflow, nhưng nó thường bị xóa vì là một câu hỏi viết kém. Tôi đã thấy nhiều câu hỏi như vậy và sau đó im lặng từ OP (đại diện thấp thông thường) khi thông tin bổ sung được yêu cầu. Đôi khi, nếu đầu vào đủ tốt cho tôi, tôi quyết định trả lời bằng một câu trả lời và nó thường nhận được một vài phiếu bầu mỗi ngày khi đang hoạt động, nhưng sau đó vài tuần, câu hỏi sẽ bị xóa / xóa và tất cả bắt đầu từ bắt đầu. Vì vậy, tôi quyết định viết Câu hỏi và Đáp này để tôi có thể tham khảo trực tiếp những câu hỏi như vậy mà không cần viết đi viết lại câu trả lời…

Một lý do khác cũng là chủ đề meta này nhắm mục tiêu đến tôi vì vậy nếu bạn có thêm thông tin đầu vào, hãy bình luận.

Câu hỏi

Làm cách nào để chuyển đổi ảnh bitmap sang ảnh nghệ thuật ASCII bằng C ++ ?

Một số ràng buộc:

  • hình ảnh thang màu xám
  • sử dụng phông chữ có khoảng cách đơn
  • giữ cho nó đơn giản (không sử dụng những thứ quá nâng cao cho các lập trình viên mới bắt đầu)

Đây là một trang Wikipedia liên quan đến nghệ thuật ASCII (cảm ơn @RogerRowland).

Đây mê cung tương tự như ASCII Chuyển đổi nghệ thuật Q&A.


Sử dụng trang wiki này làm tài liệu tham khảo, bạn có thể làm rõ bạn đang đề cập đến loại nghệ thuật ASCII nào không? Tôi nghe có vẻ giống như "Chuyển đổi hình ảnh sang văn bản", là một cách tra cứu "đơn giản" từ các pixel thang độ xám thành ký tự văn bản tương ứng, vì vậy tôi tự hỏi liệu bạn có ý gì khác không. Có vẻ như bạn đang đi để trả lời nó cho mình anyway dù .....
Roger Rowland


@RogerRowland cả đơn giản (chỉ dựa trên cường độ thang độ xám) và nâng cao hơn có tính đến cả hình dạng của các ký tự (nhưng vẫn đủ đơn giản)
Spektre 7/10/15

1
Mặc dù công việc của bạn là tuyệt vời, tôi chắc chắn sẽ đánh giá cao việc lựa chọn các mẫu có SFW hơn một chút.
kmote

@TimCastelijns Nếu bạn đọc phần mở đầu thì bạn có thể thấy đây không phải là lần đầu tiên loại câu trả lời như vậy được yêu cầu (và hầu hết những người bình chọn đều bắt đầu quen với một vài câu hỏi trước đó có liên quan nên những người còn lại chỉ bình chọn cho phù hợp), vì đây không chỉ là Hỏi & Đáp Q Tôi không lãng phí quá nhiều thời gian với phần Q (tôi thừa nhận đó là lỗi của phía tôi) đã thêm một số hạn chế cho câu hỏi nếu bạn có câu nào tốt hơn, vui lòng chỉnh sửa.
Spektre

Câu trả lời:


152

Có nhiều cách tiếp cận hơn để chuyển đổi hình ảnh sang nghệ thuật ASCII chủ yếu dựa trên việc sử dụng các phông chữ có khoảng cách đơn . Để đơn giản, tôi chỉ bám vào những điều cơ bản:

Dựa trên cường độ pixel / khu vực (đổ bóng)

Cách tiếp cận này xử lý mỗi pixel của một vùng pixel dưới dạng một chấm duy nhất. Ý tưởng là tính toán cường độ thang màu xám trung bình của dấu chấm này và sau đó thay thế nó bằng ký tự có cường độ đủ gần với cường độ đã tính. Để làm được điều đó, chúng tôi cần một số danh sách các ký tự có thể sử dụng, mỗi ký tự có cường độ được tính toán trước. Hãy gọi nó là một nhân vật map. Để chọn nhanh hơn nhân vật nào phù hợp nhất với cường độ, có hai cách:

  1. Bản đồ ký tự cường độ phân bố tuyến tính

    Vì vậy, chúng tôi chỉ sử dụng các ký tự có sự khác biệt về cường độ với cùng một bước. Nói cách khác, khi sắp xếp tăng dần thì:

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

    Ngoài ra khi ký tự của chúng tôi mapđược sắp xếp thì chúng tôi có thể tính toán ký tự trực tiếp từ cường độ (không cần tìm kiếm)

     character = map[intensity_of(dot)/constant];
  2. Bản đồ ký tự cường độ phân bố tùy ý

    Vì vậy, chúng tôi có một loạt các ký tự có thể sử dụng và cường độ của chúng. Chúng ta cần tìm cường độ gần nhất với cường độ intensity_of(dot)Vì vậy, một lần nữa nếu chúng ta sắp xếp map[], chúng ta có thể sử dụng tìm kiếm nhị phân, nếu không, chúng ta cần O(n)tìm kiếm một vòng lặp khoảng cách tối thiểu hoặc O(1)từ điển. Đôi khi để đơn giản, ký tự map[]có thể được xử lý như phân bố tuyến tính, gây ra sự biến dạng gamma nhẹ, thường không thể nhìn thấy trong kết quả trừ khi bạn biết mình cần tìm kiếm gì.

Chuyển đổi dựa trên cường độ cũng rất tốt cho hình ảnh thang xám (không chỉ đen trắng). Nếu bạn chọn dấu chấm là một pixel, kết quả sẽ lớn (một pixel -> ký tự đơn), vì vậy đối với hình ảnh lớn hơn, một vùng (nhân kích thước phông chữ) được chọn thay thế để giữ nguyên tỷ lệ khung hình và không phóng to quá mức.

Làm thế nào để làm nó:

  1. Chia đều hình ảnh thành các pixel (thang màu xám) hoặc các khu vực (hình chữ nhật) chấm s
  2. Tính toán cường độ của từng pixel / khu vực
  3. Thay thế nó bằng ký tự từ bản đồ ký tự có cường độ gần nhất

Là ký tự, mapbạn có thể sử dụng bất kỳ ký tự nào, nhưng kết quả sẽ tốt hơn nếu ký tự có các pixel phân tán đều dọc theo vùng ký tự. Để bắt đầu, bạn có thể sử dụng:

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

được sắp xếp giảm dần và giả vờ được phân phối tuyến tính.

Vì vậy, nếu cường độ của pixel / khu vực i = <0-255>thì ký tự thay thế sẽ là

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

Nếu i==0thì pixel / khu vực là màu đen, nếu i==127thì pixel / khu vực là màu xám, và nếu i==255thì pixel / khu vực là màu trắng. Bạn có thể thử nghiệm với các ký tự khác nhau bên trong map[]...

Đây là một ví dụ cổ xưa của tôi trong C ++ và 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;

Bạn cần thay thế / bỏ qua nội dung VCL trừ khi bạn sử dụng môi trường Borland / Embarcadero .

  • mm_log là bản ghi nhớ nơi văn bản được xuất ra
  • bmp là bitmap đầu vào
  • AnsiStringlà một chuỗi kiểu VCL được lập chỉ mục từ 1, không phải từ 0 như char*!!!

Đây là kết quả: Hình ảnh ví dụ về cường độ NSFW hơi

Ở bên trái là đầu ra nghệ thuật ASCII (cỡ chữ 5 pixel), và ở bên phải hình ảnh đầu vào được phóng to một vài lần. Như bạn có thể thấy, đầu ra là ký tự pixel -> lớn hơn. Nếu bạn sử dụng các khu vực lớn hơn thay vì pixel thì thu phóng sẽ nhỏ hơn, nhưng tất nhiên kết quả đầu ra kém đẹp mắt hơn. Cách tiếp cận này rất dễ dàng và nhanh chóng để viết mã / xử lý.

Khi bạn thêm những thứ nâng cao hơn như:

  • tính toán bản đồ tự động
  • lựa chọn kích thước pixel / vùng tự động
  • hiệu chỉnh tỷ lệ khung hình

Sau đó, bạn có thể xử lý các hình ảnh phức tạp hơn với kết quả tốt hơn:

Đây là kết quả theo tỷ lệ 1: 1 (thu phóng để xem các ký tự):

Ví dụ nâng cao về cường độ

Tất nhiên, để lấy mẫu khu vực, bạn sẽ mất các chi tiết nhỏ. Đây là hình ảnh có cùng kích thước với ví dụ đầu tiên được lấy mẫu với các khu vực:

Hình ảnh ví dụ nâng cao về cường độ NSFW nhẹ

Như bạn có thể thấy, điều này phù hợp hơn với hình ảnh lớn hơn.

Phù hợp ký tự (kết hợp giữa đổ bóng và nghệ thuật ASCII rắn)

Cách tiếp cận này cố gắng thay thế khu vực (không còn các chấm pixel đơn lẻ) bằng ký tự có cường độ và hình dạng tương tự. Điều này dẫn đến kết quả tốt hơn, ngay cả với các phông chữ lớn hơn được sử dụng so với cách tiếp cận trước đây. Mặt khác, cách tiếp cận này tất nhiên là chậm hơn một chút. Có nhiều cách khác để làm điều này, nhưng ý tưởng chính là tính toán sự khác biệt (khoảng cách) giữa vùng hình ảnh ( dot) và ký tự được hiển thị. Bạn có thể bắt đầu với tổng sai lệch tuyệt đối giữa các pixel một cách ngây thơ, nhưng điều đó sẽ dẫn đến kết quả không tốt lắm vì ngay cả sự thay đổi một pixel cũng sẽ làm cho khoảng cách lớn hơn. Thay vào đó, bạn có thể sử dụng tương quan hoặc các số liệu khác nhau. Thuật toán tổng thể gần giống như cách tiếp cận trước đây:

  1. Vì vậy, chia đều hình ảnh thành các khu vực hình chữ nhật (tỷ lệ xám) dot 's

    lý tưởng nhất là có cùng tỷ lệ khung hình với các ký tự phông chữ được hiển thị (nó sẽ bảo toàn tỷ lệ khung hình. Đừng quên rằng các ký tự thường chồng lên nhau một chút trên trục x)

  2. Tính toán cường độ của từng khu vực ( dot)

  3. Thay thế nó bằng một ký tự từ ký tự mapcó cường độ / hình dạng gần nhất

Làm thế nào chúng ta có thể tính toán khoảng cách giữa một ký tự và một dấu chấm? Đó là phần khó nhất của cách tiếp cận này. Trong khi thử nghiệm, tôi phát triển sự thỏa hiệp giữa tốc độ, chất lượng và sự đơn giản:

  1. Chia vùng ký tự thành các vùng

    Khu vực

    • Tính toán cường độ riêng biệt cho trái, phải, lên, xuống và vùng trung tâm của mỗi ký tự từ bảng chữ cái chuyển đổi của bạn ( map).
    • Chuẩn hóa tất cả các cường độ, do đó chúng độc lập về kích thước khu vực , i=(i*256)/(xs*ys).
  2. Xử lý hình ảnh nguồn trong các khu vực hình chữ nhật

    • (với cùng tỷ lệ khung hình với phông chữ đích)
    • Đối với mỗi khu vực, tính toán cường độ theo cách tương tự như trong dấu đầu dòng số 1
    • Tìm kết quả phù hợp nhất từ ​​các cường độ trong bảng chữ cái chuyển đổi
    • Xuất ra ký tự vừa vặn

Đây là kết quả cho font size = 7 pixel

Ví dụ về phù hợp ký tự

Như bạn có thể thấy, kết quả hiển thị đẹp mắt, ngay cả khi sử dụng kích thước phông chữ lớn hơn (ví dụ cách tiếp cận trước là với kích thước phông chữ 5 pixel). Đầu ra có cùng kích thước với hình ảnh đầu vào (không thu phóng). Kết quả tốt hơn đạt được vì các ký tự gần với hình ảnh gốc hơn, không chỉ theo cường độ, mà còn bởi hình dạng tổng thể, và do đó bạn có thể sử dụng phông chữ lớn hơn mà vẫn bảo toàn chi tiết (tất nhiên).

Đây là mã hoàn chỉnh cho ứng dụng chuyển đổi dựa trên VCL:

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

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

Nó đơn giản là một ứng dụng biểu mẫu ( Form1) với một đơn TMemo mm_txttrong đó. Nó tải một hình ảnh, "pic.bmp"và sau đó tùy theo độ phân giải, chọn phương pháp sử dụng để chuyển đổi thành văn bản được lưu vào "pic.txt"và gửi đến bản ghi nhớ để trực quan hóa.

Đối với những người không có VCL, hãy bỏ qua nội dung VCL và thay thế AnsiStringbằng bất kỳ loại chuỗi nào bạn có, cũng như Graphics::TBitmapbằng bất kỳ lớp bitmap hoặc hình ảnh nào bạn có với khả năng truy cập pixel.

Một lưu ý rất quan trọng là điều này sử dụng cài đặt của mm_txt->Font, vì vậy hãy đảm bảo bạn đã đặt:

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

để làm cho điều này hoạt động bình thường, nếu không, phông chữ sẽ không được xử lý như một khoảng cách đơn. Con lăn chuột chỉ cần thay đổi kích thước phông chữ lên / xuống để xem kết quả trên các kích thước phông chữ khác nhau.

[Ghi chú]

  • Xem hình ảnh chân dung Word
  • Sử dụng ngôn ngữ có khả năng truy cập bitmap / tệp và xuất văn bản
  • Tôi thực sự khuyên bạn nên bắt đầu với cách tiếp cận đầu tiên vì nó rất dễ dàng và đơn giản, sau đó chuyển sang cách thứ hai (có thể được thực hiện như sửa đổi của cách đầu tiên, vì vậy hầu hết mã vẫn như vậy)
  • Bạn nên tính toán với cường độ đảo ngược (pixel màu đen là giá trị tối đa) vì bản xem trước văn bản tiêu chuẩn nằm trên nền trắng, do đó dẫn đến kết quả tốt hơn nhiều.
  • bạn có thể thử nghiệm với kích thước, số lượng và bố cục của các vùng chia nhỏ hoặc sử dụng một số lưới như 3x3thay thế.

So sánh

Cuối cùng, đây là sự so sánh giữa hai cách tiếp cận trên cùng một đầu vào:

So sánh

Các hình ảnh được đánh dấu chấm màu xanh lá cây được thực hiện với phương pháp # 2 và các hình ảnh màu đỏ với # 1 , tất cả đều có kích thước phông chữ sáu pixel. Như bạn có thể thấy trên hình ảnh bóng đèn, phương pháp tiếp cận nhạy cảm với hình dạng tốt hơn nhiều (ngay cả khi phương pháp # 1 được thực hiện trên hình ảnh nguồn được thu phóng 2x).

Ứng dụng tuyệt vời

Trong khi đọc các câu hỏi mới của ngày hôm nay, tôi đã nảy ra ý tưởng về một ứng dụng thú vị lấy một vùng được chọn của màn hình và liên tục đưa nó vào bộ chuyển đổi ASCIIart và xem kết quả. Sau một giờ viết mã, nó đã hoàn thành và tôi rất hài lòng với kết quả mà tôi chỉ cần thêm nó vào đây.

OK, ứng dụng chỉ bao gồm hai cửa sổ. Cửa sổ chính đầu tiên về cơ bản là cửa sổ chuyển đổi cũ của tôi mà không có lựa chọn và xem trước hình ảnh (tất cả những thứ ở trên đều nằm trong đó). Nó chỉ có cài đặt xem trước và chuyển đổi ASCII. Cửa sổ thứ hai là một hình thức trống với bên trong trong suốt để lựa chọn vùng lấy (không có chức năng nào).

Bây giờ trong bộ đếm thời gian, tôi chỉ cần lấy khu vực đã chọn theo biểu mẫu lựa chọn, chuyển nó sang chuyển đổi và xem trước ASCIIart .

Vì vậy, bạn bao quanh một khu vực bạn muốn chuyển đổi bằng cửa sổ lựa chọn và xem kết quả trong cửa sổ chính. Nó có thể là một trò chơi, người xem, v.v. Nó giống như sau:

Ví dụ về ASCIIart grabber

Vì vậy, bây giờ tôi có thể xem ngay cả video trong ASCIIart để giải trí. Một số thực sự tốt đẹp :).

Đôi tay

Nếu bạn muốn thử thực hiện điều này trong GLSL , hãy xem phần này:


30
Bạn đã làm một công việc đáng kinh ngạc ở đây! Cảm ơn! Và tôi thích kiểm duyệt ASCII!
Ander Biguri

1
Một gợi ý để cải thiện: tìm ra các dẫn xuất có hướng, không chỉ cường độ.
Yakk - Adam Nevraumont

1
@Yakk chăm sóc để xây dựng?
taxksbl

2
@tarik không chỉ khớp về cường độ mà còn đối sánh với các dẫn xuất: hoặc, vượt qua các cạnh tăng cường. Về cơ bản cường độ không phải là thứ duy nhất mọi người nhìn thấy: họ nhìn thấy các vạch chia độ và các cạnh.
Yakk - Adam Nevraumont 13/10/15

1
@Yakk việc chia nhỏ khu vực làm những việc như vậy một cách gián tiếp. Có thể tốt hơn nữa là thực hiện xử lý các ký tự dưới dạng 3x3vùng và so sánh các DCT nhưng điều đó sẽ làm giảm hiệu suất rất nhiều, tôi nghĩ.
Spektre
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.