晒下我的神作c++视频播放器(上课没事写的)
  • 板块灌水区
  • 楼主luogu_tanbozhi
  • 当前回复36
  • 已保存回复36
  • 发布时间2024/9/14 22:23
  • 上次更新2024/9/15 07:12:21
查看原帖
晒下我的神作c++视频播放器(上课没事写的)
1321076
luogu_tanbozhi楼主2024/9/14 22:23
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <chrono>
#include <thread>
#include <cstdio>
#include <windows.h>

//作者CatGPT,luogu_tanbozhi, LuoguOItanbozhi
//压缩调0.25,0.15
//帧率60

// 扩展后的灰度字符映射:70阶灰度
const char* grayScaleChars = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. ";

// 读取PPM图像文件并返回图像数据,增加了读写加速
std::vector<unsigned char> readPPM(const std::string& filename, int& width, int& height) {
    std::ifstream file(filename, std::ios::binary);

    // 通过设置读取缓冲区来加速读取
    char buffer[4096];
    file.rdbuf()->pubsetbuf(buffer, sizeof(buffer));

    if (!file.is_open()) {
        throw std::runtime_error("无法打开文件: " + filename);
    }

    std::string format;
    file >> format; // 读取文件头 "P6"
    if (format != "P6") {
        throw std::runtime_error("不支持的PPM格式: " + format);
    }

    file >> width >> height;
    int maxColorValue;
    file >> maxColorValue; // 通常为255
    file.ignore(1); // 跳过一个换行符

    // 读取图像数据 (RGB格式)
    std::vector<unsigned char> imageData(width * height * 3); // 每个像素有3个通道 (RGB)
    file.read(reinterpret_cast<char*>(imageData.data()), imageData.size());

    return imageData;
}

// 将RGB像素转换为灰度值
unsigned char rgbToGray(unsigned char r, unsigned char g, unsigned char b) {
    return static_cast<unsigned char>(0.3 * r + 0.59 * g + 0.11 * b); // 常见的灰度转换公式
}

// 将灰度值映射为ASCII字符
char grayToAscii(unsigned char grayValue) {
    // 使用70阶灰度映射
    int index = (grayValue * 69) / 255;  // 通过除以255来映射到[0, 69]的字符索引
    return grayScaleChars[index];
}

// 显示ASCII图像,保持横纵比例
void displayAsciiImage(const std::vector<unsigned char>& imageData, int width, int height, double horizontalCompression, double verticalCompression) {
    std::string asciiImage;

    int newWidth = static_cast<int>(width * horizontalCompression);  // 水平方向压缩
    int newHeight = static_cast<int>(height * verticalCompression);  // 垂直方向压缩

    for (int y = 0; y < newHeight; ++y) {
        for (int x = 0; x < newWidth; ++x) {
            int idx = (static_cast<int>(y / verticalCompression) * width + static_cast<int>(x / horizontalCompression)) * 3; // 根据压缩率映射像素
            unsigned char r = imageData[idx];
            unsigned char g = imageData[idx + 1];
            unsigned char b = imageData[idx + 2];
            unsigned char gray = rgbToGray(r, g, b);  // 转换为灰度
            asciiImage += grayToAscii(gray);          // 转换为ASCII字符
        }
        asciiImage += '\n'; // 换行
    }
    std::system("cls"); // 清屏并将光标移至左上角
    std::cout << asciiImage;      // 输出ASCII图像
}

// 生成帧文件名
std::string generateFrameFilename(const std::string& pattern, int frameNumber) {
    char buffer[100];
    sprintf(buffer, pattern.c_str(), frameNumber);
    return std::string(buffer);
}

int main() {
    std::string pattern;
    std::cout << "请输入.ppm文件名模式 (如 a_%03d.ppm): ";
    std::cin >> pattern;

    // 设置帧率
    int fps = 30;
    std::cout << "请输入帧率 (例如 30): ";
    std::cin >> fps;
    int frameDelay = 1000 / fps;  // 每帧的时间间隔,单位:毫秒

    // 设置最大帧数
    int maxFrames = 10000;  // 假设最大帧数为10000,实际情况根据你的文件数量调整

    // 添加可调的横向和纵向压缩率
    double horizontalCompression, verticalCompression;
    std::cout << "请输入图像宽度压缩率 (例如 0.5 表示宽度缩放为原来的50%): ";
    std::cin >> horizontalCompression;
    std::cout << "请输入图像高度压缩率 (建议为1.0, 若调整比例不同可改变): ";
    std::cin >> verticalCompression;

    // 模拟多帧播放
    for (int i = 0; i < maxFrames; ++i) {
        std::string filename = generateFrameFilename(pattern, i + 1);

        // 尝试读取帧文件
        int width, height;
        std::vector<unsigned char> imageData;
        try {
            imageData = readPPM(filename, width, height);
        } catch (const std::exception& e) {
            std::cerr << "无法读取帧: " << filename << " 错误: " << e.what() << std::endl;
            break;  // 如果文件读取失败,就停止播放
        }

        auto frameStart = std::chrono::high_resolution_clock::now();

        // 显示当前帧的ASCII图像
        displayAsciiImage(imageData, width, height, horizontalCompression, verticalCompression);

        // 控制帧率
        auto frameEnd = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double, std::milli> elapsed = frameEnd - frameStart;
        if (elapsed.count() < frameDelay) {
            std::this_thread::sleep_for(std::chrono::milliseconds(frameDelay) - elapsed);
        }
    }

    return 0;
}

2024/9/14 22:23
加载中...