Hiển thị hình ảnh trong ứng dụng bảng điều khiển


85

Tôi có một ứng dụng bảng điều khiển quản lý hình ảnh. Bây giờ tôi cần một cái gì đó giống như một bản xem trước của Hình ảnh trong ứng dụng bảng điều khiển. Có cách nào để hiển thị chúng trong bảng điều khiển không?

Dưới đây là so sánh các câu trả lời dựa trên ký tự hiện tại:

Đầu vào:

nhập mô tả hình ảnh ở đây

Đầu ra:

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây


Trong bảng điều khiển, cũng như trong cửa sổ bảng điều khiển? Không. Tuy nhiên, bạn có thể bắt đầu một hộp thoại / cửa sổ riêng biệt.
Christian.K

Các ứng dụng bảng điều khiển được sử dụng chủ yếu cho các ứng dụng chỉ văn bản. Không có cách nào để hiển thị một hình ảnh. Bạn có thể khởi chạy một ứng dụng khác hiển thị hình ảnh. Ứng dụng khác này rất có thể sẽ cần hỗ trợ một tùy chọn dòng lệnh để chuyển một hình ảnh đến nó.
Lindos Pechos

Tại sao bạn sử dụng một ứng dụng bảng điều khiển? Nó chạy ở đâu? bạn luôn có thể bắt đầu một quá trình để đưa lên trình xem ảnh mặc định hoặc một cách đơn giản WinForms vv ứng dụng của riêng bạn ..
Taw

1
Tôi cần cải thiện mã của Antonín Lejsek (thật tuyệt). Có một vài màu sắc bị sai và với hiệu suất được cải thiện, tôi cũng có thể hiển thị ảnh gif động
Byyo

Câu trả lời:


55

Tôi tiếp tục chơi với mã từ @DieterMeemken. Tôi đã giảm một nửa độ phân giải theo chiều dọc và thêm phối màu qua ░▒▓. Bên trái là kết quả Dieter Meemken, bên phải là của tôi. Ở phía dưới là hình ảnh gốc được thay đổi kích thước để khớp với kết quả đầu ra. Kết quả đầu ra Trong khi chức năng chuyển đổi Malwyns rất ấn tượng, nó không sử dụng toàn bộ màu xám, thật đáng tiếc.

static int[] cColors = { 0x000000, 0x000080, 0x008000, 0x008080, 0x800000, 0x800080, 0x808000, 0xC0C0C0, 0x808080, 0x0000FF, 0x00FF00, 0x00FFFF, 0xFF0000, 0xFF00FF, 0xFFFF00, 0xFFFFFF };

public static void ConsoleWritePixel(Color cValue)
{
    Color[] cTable = cColors.Select(x => Color.FromArgb(x)).ToArray();
    char[] rList = new char[] { (char)9617, (char)9618, (char)9619, (char)9608 }; // 1/4, 2/4, 3/4, 4/4
    int[] bestHit = new int[] { 0, 0, 4, int.MaxValue }; //ForeColor, BackColor, Symbol, Score

    for (int rChar = rList.Length; rChar > 0; rChar--)
    {
        for (int cFore = 0; cFore < cTable.Length; cFore++)
        {
            for (int cBack = 0; cBack < cTable.Length; cBack++)
            {
                int R = (cTable[cFore].R * rChar + cTable[cBack].R * (rList.Length - rChar)) / rList.Length;
                int G = (cTable[cFore].G * rChar + cTable[cBack].G * (rList.Length - rChar)) / rList.Length;
                int B = (cTable[cFore].B * rChar + cTable[cBack].B * (rList.Length - rChar)) / rList.Length;
                int iScore = (cValue.R - R) * (cValue.R - R) + (cValue.G - G) * (cValue.G - G) + (cValue.B - B) * (cValue.B - B);
                if (!(rChar > 1 && rChar < 4 && iScore > 50000)) // rule out too weird combinations
                {
                    if (iScore < bestHit[3])
                    {
                        bestHit[3] = iScore; //Score
                        bestHit[0] = cFore;  //ForeColor
                        bestHit[1] = cBack;  //BackColor
                        bestHit[2] = rChar;  //Symbol
                    }
                }
            }
        }
    }
    Console.ForegroundColor = (ConsoleColor)bestHit[0];
    Console.BackgroundColor = (ConsoleColor)bestHit[1];
    Console.Write(rList[bestHit[2] - 1]);
}


public static void ConsoleWriteImage(Bitmap source)
{
    int sMax = 39;
    decimal percent = Math.Min(decimal.Divide(sMax, source.Width), decimal.Divide(sMax, source.Height));
    Size dSize = new Size((int)(source.Width * percent), (int)(source.Height * percent));   
    Bitmap bmpMax = new Bitmap(source, dSize.Width * 2, dSize.Height);
    for (int i = 0; i < dSize.Height; i++)
    {
        for (int j = 0; j < dSize.Width; j++)
        {
            ConsoleWritePixel(bmpMax.GetPixel(j * 2, i));
            ConsoleWritePixel(bmpMax.GetPixel(j * 2 + 1, i));
        }
        System.Console.WriteLine();
    }
    Console.ResetColor();
}

sử dụng:

Bitmap bmpSrc = new Bitmap(@"HuwnC.gif", true);    
ConsoleWriteImage(bmpSrc);

BIÊN TẬP

Khoảng cách màu là một chủ đề phức tạp ( đây , đây và các liên kết trên các trang đó ...). Tôi đã cố gắng tính toán khoảng cách trong YUV và kết quả tệ hơn so với RGB. Chúng có thể tốt hơn với Lab và DeltaE, nhưng tôi đã không thử điều đó. Khoảng cách trong RGB có vẻ là đủ tốt. Trên thực tế, kết quả rất giống nhau cho cả khoảng cách euclidean và manhattan trong không gian màu RGB, vì vậy tôi nghi ngờ rằng có quá ít màu để lựa chọn.

Phần còn lại chỉ là sự so sánh thô bạo của màu sắc với tất cả sự kết hợp của màu sắc và hoa văn (= biểu tượng). Tôi đã nêu tỷ lệ lấp đầy cho ░▒▓█ là 1/4, 2/4, 3/4 và 4/4. Trong trường hợp đó, biểu tượng thứ ba trên thực tế là thừa so với biểu tượng đầu tiên. Nhưng nếu tỷ lệ không đồng nhất như vậy (phụ thuộc vào phông chữ), kết quả có thể thay đổi, vì vậy tôi để nó ở đó để cải thiện trong tương lai. Màu trung bình của biểu tượng được tính bằng giá trị trung bình của foregroudColor và backgroundColor theo tỷ lệ tô màu. Nó giả định màu sắc tuyến tính, những gì cũng đơn giản hóa lớn. Vì vậy, vẫn còn chỗ để cải thiện.


Cảm ơn bạn @fubo. Btw, tôi đã thử nghiệm với RGB và Lab đã được hiệu chỉnh gamma và cả hai đều được cải thiện. Nhưng tỷ lệ lấp đầy phải được đặt để phù hợp với phông chữ đã sử dụng và nó hoàn toàn không hoạt động đối với phông chữ truetype. Vì vậy, nó sẽ không còn là một kích thước phù hợp với tất cả sự chú ý.
Antonín Lejsek

88

Mặc dù hiển thị hình ảnh trong bảng điều khiển không phải là mục đích sử dụng bảng điều khiển, bạn chắc chắn có thể hack mọi thứ, vì cửa sổ bảng điều khiển chỉ là một cửa sổ, giống như bất kỳ cửa sổ nào khác.

Trên thực tế, một khi tôi đã bắt đầu phát triển một thư viện điều khiển văn bản cho các ứng dụng bảng điều khiển có hỗ trợ đồ họa. Tôi chưa bao giờ hoàn thành điều đó, mặc dù tôi có một bản demo bằng chứng khái niệm đang hoạt động:

Điều khiển văn bản với hình ảnh

Và nếu bạn có được kích thước phông chữ của bảng điều khiển, bạn có thể đặt hình ảnh rất chính xác.

Đây là cách bạn có thể làm điều đó:

static void Main(string[] args)
{
    Console.WriteLine("Graphics in console window!");

    Point location = new Point(10, 10);
    Size imageSize = new Size(20, 10); // desired image size in characters

    // draw some placeholders
    Console.SetCursorPosition(location.X - 1, location.Y);
    Console.Write(">");
    Console.SetCursorPosition(location.X + imageSize.Width, location.Y);
    Console.Write("<");
    Console.SetCursorPosition(location.X - 1, location.Y + imageSize.Height - 1);
    Console.Write(">");
    Console.SetCursorPosition(location.X + imageSize.Width, location.Y + imageSize.Height - 1);
    Console.WriteLine("<");

    string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonPictures), @"Sample Pictures\tulips.jpg");
    using (Graphics g = Graphics.FromHwnd(GetConsoleWindow()))
    {
        using (Image image = Image.FromFile(path))
        {
            Size fontSize = GetConsoleFontSize();

            // translating the character positions to pixels
            Rectangle imageRect = new Rectangle(
                location.X * fontSize.Width,
                location.Y * fontSize.Height,
                imageSize.Width * fontSize.Width,
                imageSize.Height * fontSize.Height);
            g.DrawImage(image, imageRect);
        }
    }
}

Đây là cách bạn có thể có được kích thước phông chữ bảng điều khiển hiện tại:

private static Size GetConsoleFontSize()
{
    // getting the console out buffer handle
    IntPtr outHandle = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, 
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        IntPtr.Zero,
        OPEN_EXISTING,
        0,
        IntPtr.Zero);
    int errorCode = Marshal.GetLastWin32Error();
    if (outHandle.ToInt32() == INVALID_HANDLE_VALUE)
    {
        throw new IOException("Unable to open CONOUT$", errorCode);
    }

    ConsoleFontInfo cfi = new ConsoleFontInfo();
    if (!GetCurrentConsoleFont(outHandle, false, cfi))
    {
        throw new InvalidOperationException("Unable to get font information.");
    }

    return new Size(cfi.dwFontSize.X, cfi.dwFontSize.Y);            
}

Và các lệnh gọi, hằng số và kiểu WinApi bổ sung được yêu cầu:

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetConsoleWindow();

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateFile(
    string lpFileName,
    int dwDesiredAccess,
    int dwShareMode,
    IntPtr lpSecurityAttributes,
    int dwCreationDisposition,
    int dwFlagsAndAttributes,
    IntPtr hTemplateFile);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetCurrentConsoleFont(
    IntPtr hConsoleOutput,
    bool bMaximumWindow,
    [Out][MarshalAs(UnmanagedType.LPStruct)]ConsoleFontInfo lpConsoleCurrentFont);

[StructLayout(LayoutKind.Sequential)]
internal class ConsoleFontInfo
{
    internal int nFont;
    internal Coord dwFontSize;
}

[StructLayout(LayoutKind.Explicit)]
internal struct Coord
{
    [FieldOffset(0)]
    internal short X;
    [FieldOffset(2)]
    internal short Y;
}

private const int GENERIC_READ = unchecked((int)0x80000000);
private const int GENERIC_WRITE = 0x40000000;
private const int FILE_SHARE_READ = 1;
private const int FILE_SHARE_WRITE = 2;
private const int INVALID_HANDLE_VALUE = -1;
private const int OPEN_EXISTING = 3;

Và kết quả:

[Đồ họa trong bảng điều khiển


2
Wow, điều đó thực sự thú vị! Bạn có thể vui lòng giải thích một chút ý của bạn là tôi chưa bao giờ hoàn thành điều đó, mặc dù tôi có một bản demo bằng chứng khái niệm đang hoạt động ? Bất kỳ nhược điểm hoặc chỉ đánh bóng bị thiếu ..?
TaW

Có nghĩa là dự án rất dở dang. Tôi đã tạo kiến ​​trúc cơ bản, môi trường OO hướng sự kiện cơ sở, hỗ trợ chuột, máy bơm thông báo, v.v. Nhưng ngay cả những điều khiển cơ bản nhất như Button, TextBoxv.v. vẫn bị thiếu. Ước mơ của tôi là tạo ra một hỗ trợ XAML khá đầy đủ với liên kết dữ liệu và với triết lý "nhúng bất cứ thứ gì vào bất kỳ thứ gì" giống như WPF. Nhưng tôi rất xa mà ... tốt, tại thời điểm này :)
György Koszeg

3
OK, tôi hiểu rồi, nhưng mã có vẻ như nó có thể được sử dụng cho bất kỳ dự án nào kém hoàn thiện hơn, ít tham vọng hơn, phải không?
TaW

1
Chà, ở dạng hiện tại ... không hẳn. Nhưng tôi dự định xuất bản nó trên GitHub sau khi tôi làm cho nó ổn định và mạch lạc.
György Kőszeg

Điều này trông thực sự tuyệt vời, ngoại trừ sự cần thiết của việc sử dụng DLL không được quản lý. Ngoài ra, Làm thế nào chúng ta có thể theo dõi sự phát triển của các thư viện?
một chiếc khăn bịt tai

56

Nếu bạn sử dụng ASCII 219 (█) hai lần, bạn sẽ có một cái gì đó giống như pixel (██). Bây giờ bạn bị giới hạn bởi số lượng pixel và số lượng màu trong ứng dụng bảng điều khiển của bạn.

  • nếu bạn giữ cài đặt mặc định, bạn có khoảng 39x39 pixel, nếu muốn nhiều hơn, bạn có thể thay đổi kích thước bảng điều khiển của mình với Console.WindowHeight = resSize.Height + 1;Console.WindowWidth = resultSize.Width * 2;

  • bạn phải giữ tỷ lệ khung hình của hình ảnh càng xa càng tốt, vì vậy bạn sẽ không có 39x39 trong hầu hết các trường hợp

  • Malwyn đã đăng một phương pháp hoàn toàn bị đánh giá thấp để chuyển đổi System.Drawing.ColorsangSystem.ConsoleColor

vì vậy cách tiếp cận của tôi sẽ là

using System.Drawing;

public static int ToConsoleColor(System.Drawing.Color c)
{
    int index = (c.R > 128 | c.G > 128 | c.B > 128) ? 8 : 0;
    index |= (c.R > 64) ? 4 : 0;
    index |= (c.G > 64) ? 2 : 0;
    index |= (c.B > 64) ? 1 : 0;
    return index;
}

public static void ConsoleWriteImage(Bitmap src)
{
    int min = 39;
    decimal pct = Math.Min(decimal.Divide(min, src.Width), decimal.Divide(min, src.Height));
    Size res = new Size((int)(src.Width * pct), (int)(src.Height * pct));
    Bitmap bmpMin = new Bitmap(src, res);
    for (int i = 0; i < res.Height; i++)
    {
        for (int j = 0; j < res.Width; j++)
        {
            Console.ForegroundColor = (ConsoleColor)ToConsoleColor(bmpMin.GetPixel(j, i));
            Console.Write("██");
        }
        System.Console.WriteLine();
    }
}

vì vậy bạn có thể

ConsoleWriteImage(new Bitmap(@"C:\image.gif"));

đầu vào mẫu:

nhập mô tả hình ảnh ở đây

đầu ra mẫu:

nhập mô tả hình ảnh ở đây


7
@willywonka_dailyblah - Đó là xúc tu màu tím trong Ngày xúc tu. Không phải Doom
Blaatz0r

@ Blaatz0r i có nghĩa là Doom-như đồ họa ... Imma đứa trẻ mới về teh khối tôi chỉ biết Doom

3
Tất cả đều được tha thứ :-p. Nếu bạn có cơ hội hãy thử Ngày nếu xúc tu của nó là một trò chơi tuyệt vời cũ nhưng tuyệt vời.
Blaatz0r

37

thật là vui. Cảm ơn fubo , tôi đã thử giải pháp của bạn và có thể tăng độ phân giải của bản xem trước lên 4 (2x2).

Tôi thấy rằng bạn có thể đặt màu nền cho từng ký tự riêng lẻ. Vì vậy, thay vì sử dụng hai ký tự ASCII 219 (█), tôi đã sử dụng ASCII 223 (▀) hai lần với các Màu tiền cảnh và hậu cảnh khác nhau. Điều đó chia Pixel lớn (██) thành 4 điểm ảnh con như thế này (▀▄).

Trong ví dụ này, tôi đặt cả hai hình ảnh bên cạnh nhau, vì vậy bạn có thể dễ dàng thấy sự khác biệt:

nhập mô tả hình ảnh ở đây

Đây là mã:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace ConsoleWithImage
{
  class Program
  {

    public static void ConsoleWriteImage(Bitmap bmpSrc)
    {
        int sMax = 39;
        decimal percent = Math.Min(decimal.Divide(sMax, bmpSrc.Width), decimal.Divide(sMax, bmpSrc.Height));
        Size resSize = new Size((int)(bmpSrc.Width * percent), (int)(bmpSrc.Height * percent));
        Func<System.Drawing.Color, int> ToConsoleColor = c =>
        {
            int index = (c.R > 128 | c.G > 128 | c.B > 128) ? 8 : 0;
            index |= (c.R > 64) ? 4 : 0;
            index |= (c.G > 64) ? 2 : 0;
            index |= (c.B > 64) ? 1 : 0;
            return index;
        };
        Bitmap bmpMin = new Bitmap(bmpSrc, resSize.Width, resSize.Height);
        Bitmap bmpMax = new Bitmap(bmpSrc, resSize.Width * 2, resSize.Height * 2);
        for (int i = 0; i < resSize.Height; i++)
        {
            for (int j = 0; j < resSize.Width; j++)
            {
                Console.ForegroundColor = (ConsoleColor)ToConsoleColor(bmpMin.GetPixel(j, i));
                Console.Write("██");
            }

            Console.BackgroundColor = ConsoleColor.Black;
            Console.Write("    ");

            for (int j = 0; j < resSize.Width; j++)
            {
                Console.ForegroundColor = (ConsoleColor)ToConsoleColor(bmpMax.GetPixel(j * 2, i * 2));
                Console.BackgroundColor = (ConsoleColor)ToConsoleColor(bmpMax.GetPixel(j * 2, i * 2 + 1));
                Console.Write("▀");

                Console.ForegroundColor = (ConsoleColor)ToConsoleColor(bmpMax.GetPixel(j * 2 + 1, i * 2));
                Console.BackgroundColor = (ConsoleColor)ToConsoleColor(bmpMax.GetPixel(j * 2 + 1, i * 2 + 1));
                Console.Write("▀");
            }
            System.Console.WriteLine();
        }
    }

    static void Main(string[] args)
    {
        System.Console.WindowWidth = 170;
        System.Console.WindowHeight = 40;

        Bitmap bmpSrc = new Bitmap(@"image.bmp", true);

        ConsoleWriteImage(bmpSrc);

        System.Console.ReadLine();
    }
  }
}

Để chạy ví dụ, bitmap "image.bmp" phải nằm trong cùng thư mục với tệp thực thi. Tôi đã tăng kích thước của bảng điều khiển, kích thước của bản xem trước vẫn là 39 và có thể được thay đổi tại int sMax = 39;.

Giải pháp từ taffer cũng rất tuyệt. Hai bạn có sự ủng hộ của tôi ...


23

Tôi đang đọc về không gian màu và không gian LAB có vẻ là một lựa chọn tốt cho bạn (xem câu hỏi này: Tìm “khoảng cách” chính xác giữa các màuThuật toán để kiểm tra độ tương đồng của các màu )

Trích dẫn trang CIELAB của Wikipedia , ưu điểm của không gian màu này là:

Không giống như các mô hình màu RGB và CMYK, màu Lab được thiết kế để gần đúng với tầm nhìn của con người. Nó khao khát sự đồng nhất về mặt tri giác và thành phần L của nó gần giống với nhận thức của con người về sự nhẹ nhàng. Do đó, nó có thể được sử dụng để thực hiện các hiệu chỉnh cân bằng màu chính xác bằng cách sửa đổi các đường cong đầu ra trong các thành phần a và b.

Để đo khoảng cách giữa các màu, bạn có thể sử dụng khoảng cách Delta E.

Với điều này bạn có thể xấp xỉ tốt hơn từ Colorđể ConsoleColor:

Đầu tiên, bạn có thể xác định một CieLablớp để đại diện cho màu sắc trong không gian này:

public class CieLab
{
    public double L { get; set; }
    public double A { get; set; }
    public double B { get; set; }

    public static double DeltaE(CieLab l1, CieLab l2)
    {
        return Math.Pow(l1.L - l2.L, 2) + Math.Pow(l1.A - l2.A, 2) + Math.Pow(l1.B - l2.B, 2);
    }

    public static CieLab Combine(CieLab l1, CieLab l2, double amount)
    {
        var l = l1.L * amount + l2.L * (1 - amount);
        var a = l1.A * amount + l2.A * (1 - amount);
        var b = l1.B * amount + l2.B * (1 - amount);

        return new CieLab { L = l, A = a, B = b };
    }
}

Có hai phương pháp tĩnh, một phương pháp để đo khoảng cách bằng cách sử dụng Delta E ( DeltaE) và phương pháp khác để kết hợp hai màu xác định số lượng của mỗi màu ( Combine).

Và để chuyển đổi từ RGBsang LABbạn có thể sử dụng phương pháp sau (từ đây ):

public static CieLab RGBtoLab(int red, int green, int blue)
{
    var rLinear = red / 255.0;
    var gLinear = green / 255.0;
    var bLinear = blue / 255.0;

    double r = rLinear > 0.04045 ? Math.Pow((rLinear + 0.055) / (1 + 0.055), 2.2) : (rLinear / 12.92);
    double g = gLinear > 0.04045 ? Math.Pow((gLinear + 0.055) / (1 + 0.055), 2.2) : (gLinear / 12.92);
    double b = bLinear > 0.04045 ? Math.Pow((bLinear + 0.055) / (1 + 0.055), 2.2) : (bLinear / 12.92);

    var x = r * 0.4124 + g * 0.3576 + b * 0.1805;
    var y = r * 0.2126 + g * 0.7152 + b * 0.0722;
    var z = r * 0.0193 + g * 0.1192 + b * 0.9505;

    Func<double, double> Fxyz = t => ((t > 0.008856) ? Math.Pow(t, (1.0 / 3.0)) : (7.787 * t + 16.0 / 116.0));

    return new CieLab
    {
        L = 116.0 * Fxyz(y / 1.0) - 16,
        A = 500.0 * (Fxyz(x / 0.9505) - Fxyz(y / 1.0)),
        B = 200.0 * (Fxyz(y / 1.0) - Fxyz(z / 1.0890))
    };
}

Ý tưởng là sử dụng các ký tự bóng râm như @AntoninLejsek do ('█', '▓', '▒', '░'), điều này cho phép bạn có được hơn 16 màu kết hợp các màu của bảng điều khiển (sử dụng Combinephương pháp).

Tại đây, chúng tôi có thể thực hiện một số cải tiến bằng cách tính toán trước các màu sẽ sử dụng:

class ConsolePixel
{
    public char Char { get; set; }

    public ConsoleColor Forecolor { get; set; }
    public ConsoleColor Backcolor { get; set; }
    public CieLab Lab { get; set; }
}

static List<ConsolePixel> pixels;
private static void ComputeColors()
{
    pixels = new List<ConsolePixel>();

    char[] chars = { '█', '▓', '▒', '░' };

    int[] rs = { 0, 0, 0, 0, 128, 128, 128, 192, 128, 0, 0, 0, 255, 255, 255, 255 };
    int[] gs = { 0, 0, 128, 128, 0, 0, 128, 192, 128, 0, 255, 255, 0, 0, 255, 255 };
    int[] bs = { 0, 128, 0, 128, 0, 128, 0, 192, 128, 255, 0, 255, 0, 255, 0, 255 };

    for (int i = 0; i < 16; i++)
        for (int j = i + 1; j < 16; j++)
        {
            var l1 = RGBtoLab(rs[i], gs[i], bs[i]);
            var l2 = RGBtoLab(rs[j], gs[j], bs[j]);

            for (int k = 0; k < 4; k++)
            {
                var l = CieLab.Combine(l1, l2, (4 - k) / 4.0);

                pixels.Add(new ConsolePixel
                {
                    Char = chars[k],
                    Forecolor = (ConsoleColor)i,
                    Backcolor = (ConsoleColor)j,
                    Lab = l
                });
            }
        }
}

Một cải tiến khác có thể là truy cập trực tiếp vào dữ liệu hình ảnh bằng cách sử dụng LockBitsthay vì sử dụng GetPixel.

CẬP NHẬT : Nếu hình ảnh có các phần có cùng màu, bạn có thể tăng tốc đáng kể quá trình vẽ các đoạn ký tự có cùng màu, thay vì các ký tự riêng lẻ:

public static void DrawImage(Bitmap source)
{
    int width = Console.WindowWidth - 1;
    int height = (int)(width * source.Height / 2.0 / source.Width);

    using (var bmp = new Bitmap(source, width, height))
    {
        var unit = GraphicsUnit.Pixel;
        using (var src = bmp.Clone(bmp.GetBounds(ref unit), PixelFormat.Format24bppRgb))
        {
            var bits = src.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, src.PixelFormat);
            byte[] data = new byte[bits.Stride * bits.Height];

            Marshal.Copy(bits.Scan0, data, 0, data.Length);

            for (int j = 0; j < height; j++)
            {
                StringBuilder builder = new StringBuilder();
                var fore = ConsoleColor.White;
                var back = ConsoleColor.Black;

                for (int i = 0; i < width; i++)
                {
                    int idx = j * bits.Stride + i * 3;
                    var pixel = DrawPixel(data[idx + 2], data[idx + 1], data[idx + 0]);


                    if (pixel.Forecolor != fore || pixel.Backcolor != back)
                    {
                        Console.ForegroundColor = fore;
                        Console.BackgroundColor = back;
                        Console.Write(builder);

                        builder.Clear();
                    }

                    fore = pixel.Forecolor;
                    back = pixel.Backcolor;
                    builder.Append(pixel.Char);
                }

                Console.ForegroundColor = fore;
                Console.BackgroundColor = back;
                Console.WriteLine(builder);
            }

            Console.ResetColor();
        }
    }
}

private static ConsolePixel DrawPixel(int r, int g, int b)
{
    var l = RGBtoLab(r, g, b);

    double diff = double.MaxValue;
    var pixel = pixels[0];

    foreach (var item in pixels)
    {
        var delta = CieLab.DeltaE(l, item.Lab);
        if (delta < diff)
        {
            diff = delta;
            pixel = item;
        }
    }

    return pixel;
}

Cuối cùng, hãy gọi DrawImagenhư vậy:

static void Main(string[] args)
{
    ComputeColors();

    Bitmap image = new Bitmap("image.jpg", true);
    DrawImage(image);

}

Hình ảnh kết quả:

Console1

Console2



Các giải pháp sau đây không dựa trên ký tự nhưng cung cấp hình ảnh chi tiết đầy đủ


Bạn có thể vẽ qua bất kỳ cửa sổ nào bằng cách sử dụng trình xử lý của nó để tạo một Graphicsđối tượng. Để có được trình xử lý của ứng dụng bảng điều khiển, bạn có thể nhập GetConsoleWindow:

[DllImport("kernel32.dll", EntryPoint = "GetConsoleWindow", SetLastError = true)]
private static extern IntPtr GetConsoleHandle();

Sau đó, tạo một đồ họa với trình xử lý (using Graphics.FromHwnd) và vẽ hình ảnh bằng các phương thức trong Graphicsđối tượng, ví dụ:

static void Main(string[] args)
{            
    var handler = GetConsoleHandle();

    using (var graphics = Graphics.FromHwnd(handler))
    using (var image = Image.FromFile("img101.png"))
        graphics.DrawImage(image, 50, 50, 250, 200);
}

Phiên bản 1

Điều này trông ổn nhưng nếu bảng điều khiển được thay đổi kích thước hoặc cuộn, hình ảnh sẽ biến mất do các cửa sổ được làm mới (có thể thực hiện một số loại cơ chế để vẽ lại hình ảnh có thể thực hiện được trong trường hợp của bạn).


Một giải pháp khác là nhúng một window ( Form) vào ứng dụng console. Để làm điều này, bạn phải nhập SetParent(và MoveWindowdi chuyển cửa sổ bên trong bảng điều khiển):

[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

[DllImport("user32.dll", SetLastError = true)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

Sau đó, bạn chỉ cần tạo một thuộc tính Formvà đặt thuộc BackgroundImagetính cho hình ảnh mong muốn (thực hiện trên Threadhoặc Taskđể tránh chặn bảng điều khiển):

static void Main(string[] args)
{
    Task.Factory.StartNew(ShowImage);

    Console.ReadLine();
}

static void ShowImage()
{
    var form = new Form
    {                
        BackgroundImage = Image.FromFile("img101.png"),
        BackgroundImageLayout = ImageLayout.Stretch
    };

    var parent = GetConsoleHandle();
    var child = form.Handle;

    SetParent(child, parent);
    MoveWindow(child, 50, 50, 250, 200, true);

    Application.Run(form);
}

Phiên bản 2

Tất nhiên bạn có thể đặt FormBorderStyle = FormBorderStyle.Noneđể ẩn đường viền cửa sổ (hình bên phải)

Trong trường hợp này, bạn có thể thay đổi kích thước bảng điều khiển và hình ảnh / cửa sổ vẫn ở đó.

Một lợi ích với cách tiếp cận này là bạn có thể định vị cửa sổ ở nơi bạn muốn và thay đổi hình ảnh bất kỳ lúc nào chỉ bằng cách thay đổi thuộc BackgroundImagetính.


Cảm ơn bạn đã nỗ lực nhưng cách tiếp cận của bạn chậm hơn 6 lần so với giải pháp của Antonín Lejsek. Dù sao kết quả Lap rất thú vị.
Byyo

4

Không có cách nào trực tiếp. Nhưng bạn có thể thử sử dụng công cụ chuyển đổi hình ảnh thành nghệ thuật như thế này


:-) tuy nhiên, lưu ý rằng khả năng màu của bảng điều khiển (cửa sổ) cũng khá hạn chế. Vì vậy, hiệu ứng "mờ dần", v.v. thậm chí không thể thực hiện được.
Christian.K

1
Vâng, nó phù hợp với độ phân giải: P
DarkWanderer

1
@ Christian.K Câu trả lời của Antonín Lejsek có thể làm mờ dần đi
Byyo

1

Có, bạn có thể làm được, nếu bạn kéo dài câu hỏi một chút bằng cách mở một Formtừ trong ứng dụng Console.

Đây là cách bạn có thể yêu cầu ứng dụng bảng điều khiển mở biểu mẫu và hiển thị hình ảnh:

  • bao gồm hai tài liệu tham khảo này trong dự án của bạn: System.DrawingSystem.Windows.Forms
  • bao gồm cả hai không gian tên:

using System.Windows.Forms;
using System.Drawing;

Xem bài đăng này về cách làm điều đó !

Bây giờ tất cả những gì bạn cần để thêm một cái gì đó như sau:

Form form1 = new Form();
form1.BackgroundImage = bmp;
form1.ShowDialog();

Tất nhiên bạn cũng có thể sử dụng PictureBox..

Và bạn có thể sử dụng form1.Show();để giữ cho bảng điều khiển hoạt động trong khi bản xem trước hiển thị ..

Bài gốc: Tất nhiên bạn không thể hiển thị đúng hình ảnh bên trong cửa sổ 25x80; ngay cả khi bạn sử dụng một cửa sổ lớn hơn và chặn đồ họa, nó sẽ không phải là một bản xem trước mà là một mớ hỗn độn!

Cập nhật: Có vẻ như sau tất cả GDI, bạn có thể vẽ một hình ảnh vào Biểu mẫu bảng điều khiển; xem câu trả lời của taffer!

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.