Skip to content

Instantly share code, notes, and snippets.

@noahlias
Created August 16, 2023 02:28
Show Gist options
  • Save noahlias/df5de36c02e62e0a3b5aed1a58f5a80e to your computer and use it in GitHub Desktop.
Save noahlias/df5de36c02e62e0a3b5aed1a58f5a80e to your computer and use it in GitHub Desktop.
video_to_ascii
#include <SFML/Audio.hpp>
#include <chrono>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <thread>
using namespace std;
using namespace cv;
void playAudio(const string& audio_path) {
sf::Music music;
if (!music.openFromFile(audio_path)) {
cout << "Error loading audio from video." << endl;
return;
}
music.play();
while (music.getStatus() == sf::Music::Playing) {
std::this_thread::sleep_for(
std::chrono::milliseconds(10)); // Sleep for a short duration
}
}
string pixelToASCII(int pixel_intensity) {
// const string ASCII_CHARS = "@$%#&!*+=-_. ";
// const string ASCII_CHARS =
// "@B%8WM#*oahkbdpwmZO0QCJYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. "; const
// string ASCII_CHARS = "@#&!*+=-_. ";
const string ASCII_CHARS = " ._-=+*!&#%$@";
string s =
string(1, ASCII_CHARS[pixel_intensity * ASCII_CHARS.length() / 256]);
return s;
}
int main(int argc, char** argv) {
if (argc < 3) {
cout << "Usage: " << argv[0] << " <video_path> <audio_path>" << endl;
return -1;
}
string video_path = argv[1];
string audio_path = argv[2];
VideoCapture cap(video_path);
double fps = cap.get(CAP_PROP_FPS);
// cout << fps << endl;
int frame_duration_ms = 1000 / fps;
int width = 85;
int height = 30;
int frame_width = cap.get(CAP_PROP_FRAME_WIDTH);
int frame_height = cap.get(CAP_PROP_FRAME_HEIGHT);
// cout << frame_width << " " << frame_height << endl;
// 4096 2160
// 751 × 944
// height = (width * frame_height / frame_width) * 0.4194;
Mat frame, gray_frame, resized_frame;
auto video_start_time = std::chrono::high_resolution_clock::now();
bool isFirstFrame = true;
for (int frame_number = 0;; ++frame_number) {
cap >> frame;
if (frame.empty()) break;
cv::cvtColor(frame, gray_frame, cv::COLOR_BGR2GRAY);
resize(gray_frame, resized_frame, Size(width, height), 0, 0, INTER_LINEAR);
string ascii_frame;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
ascii_frame += pixelToASCII(resized_frame.at<uchar>(i, j));
}
ascii_frame += "\n";
}
system("clear");
cout << ascii_frame;
if (isFirstFrame) {
std::thread audioThread(playAudio, audio_path);
audioThread.detach();
isFirstFrame = false;
}
auto current_time = std::chrono::high_resolution_clock::now();
auto since_video_start =
std::chrono::duration_cast<std::chrono::milliseconds>(current_time -
video_start_time);
auto next_frame_time = frame_number * frame_duration_ms;
if (since_video_start.count() < next_frame_time) {
std::this_thread::sleep_for(std::chrono::milliseconds(
next_frame_time - since_video_start.count()));
}
}
return 0;
}
@noahlias
Copy link
Author

build.sh

You need install SFML and OpenCV first.

g++ -std=c++11 `pkg-config --cflags opencv4` `pkg-config --libs opencv4` -lsfml-audio -o video_to_ascii main.cpp

Getting Started

Download the video and mp3. Then run the program

./video_to_ascii demo.mp4 demo.mp3

And you get the gif.
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment