Reputation: 21
I made a program that turns a video into a resized ASCII animation, but the Mat frame
only captures a portion of the top left corner of the full video. The depth and channel of the Mat
is 0 and 1, accordingly. The program also doesn't work without the at<uchar>
, even though the Mat
returns as empty?
#include <iostream>
#include <vector>
#include <chrono>
#include <thread>
#include <Windows.h>
#include <mmsystem.h>
#include <opencv2/opencv.hpp>
#pragma comment(lib, "winmm.lib")
void checkForOpenCV()
{
if(cv::getCPUTickCount == 0)
{
std::cout << "OpenCV is not detected." << '\n';
}
else
{
std::cout << "OpenCV is detected." << '\n';
}
}
cv::VideoCapture openVid(std::string &filePath)
{
cv::VideoCapture cap{filePath};
if(!cap.isOpened())
{
std::cout << "The file cannot be accessed." << '\n';
exit(-1);
}
else
{
std::cout << "File is accessed." << '\n';
}
return cap;
}
char brightness2Ascii(int brightness, std::string asciiCharset)
{
int index(brightness * asciiCharset.size() / 256);
return asciiCharset[index];
}
int main()
{
checkForOpenCV();
std::string filePath{R"(C:\Users\Harold.DESKTOP-UJ6F4M4\Videos\video.mp4)"};
cv::VideoCapture cap{openVid(filePath)};
double fps{cap.get(cv::CAP_PROP_FPS)};
std::cout << fps << '\n';
int width(cap.get(cv::CAP_PROP_FRAME_WIDTH));
int height(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
int aspectRatio{12};
std::cout << width << " x " << height << '\n';
std::vector<cv::Mat> totalFrames{};
cv::Mat frame{};
int depth{frame.depth()};
int channels{frame.channels()};
while(cap.read(frame))
{
cv::resize(frame, frame, cv::Size(width / aspectRatio, height / aspectRatio), 0, 0, cv::INTER_LINEAR);
totalFrames.push_back(frame.clone());
}
cap.release();
for(const auto &frame:totalFrames)
{
std::string asciiArt{};
for(int i{0}; i < frame.rows; i++)
{
for(int j{0}; j < frame.cols; j++)
{
const std::string asciiCharset{" .'-=*%#@"}; // {" .'-=*%#@"} {"@ "}
int brightness{frame.at<uchar>(i, j)};
char pixelChar{brightness2Ascii(brightness, asciiCharset)};
asciiArt += pixelChar;
}
asciiArt += '\n';
}
std::cout << depth << '\n' << channels << '\n';
std::cout << asciiArt;
cv::waitKey(fps / 2);
}
return 0;
}
I tried creating a separate Mat resizedFrame
, as well as declaring a separate width and height for the asciiChar
to no avail. Initially, I thought the problem occured from resizing the frame after preloading all of it, but after relocating the resize()
in the preloading loop, I'm not so sure anymore.
Upvotes: 1
Views: 113
Reputation: 21
Turns out I needed to convert the frame
into a GRAY Mat
if I'll use at<uchar>
alongside it. I didn't do that because the video was black and white already, but OpenCV turned it into BGR anyway. For non-gray Mats, use at<Vec3b>()
instead.
for(const auto &frame : totalFrames)
{
cv::cvtColor(frame, grayFrame, cv::COLOR_BGR2GRAY);
cv::resize(grayFrame, grayFrame, cv::Size(2 * width / aspectRatio, height / aspectRatio), 0, 0, cv::INTER_LINEAR);
std::string asciiArt{};
for(int i{0}; i < grayFrame.rows; i++)
{
for(int j{0}; j < grayFrame.cols; j++)
{
const std::string asciiCharset{".'-=*%#@"}; // {" .'-=*%#@"} {"@ "}
int brightness{grayFrame.at<uchar>(i, j)};
char pixelChar{brightness2Ascii(brightness, asciiCharset)};
asciiArt += pixelChar;
}
asciiArt += '\n';
}
std::cout << asciiArt;
Sleep(1000 / fps);
}
Upvotes: 1