Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #define TEXT_ENABLE_MACROS_USING_THIS _text
- #include <Text.hpp>
- #include <memory.hpp>
- #include <fileio.hpp>
- #include <opencv2/opencv.hpp>
- #include <windows.h>
- #include <commdlg.h> //for the 'open file' dialogue
- #include <string.h>
- #define FILTER_STR "\
- *.* (All Files)\0*.*\0\
- *.png (Portable Network Graphics Image Files)\0*.png\0\
- *.jpg (JPEG Image Files)\0*.jpg\0\
- *.bmp (Windows Bitmap Image Files)\0*.bmp\0\
- *.mp4 (MPEG-4 Video Files)\0*.mp4\0\
- *.mov (QuickTime Movie Video Files)\0*.mov\0\
- *.mkv (Matroska Video Files)\0*.mkv\0\
- *.avi (Audio Video Interleave Video Files)\0*.avi\0\
- "
- //the resolution of the largest monitor cluster is 164x81 chars,
- //and with semigraphics chars of 2x3, that makes for 328x243 (approx. 4:3).
- //so if the frame width and height is exactly 328x243, resizing is redundant
- #define TGT_X (328) //(TGT, short for target)
- #define TGT_Y (243)
- #define TGT_LEN (TGT_X*TGT_Y)
- #ifndef _DEBUG
- //#define _DEBUG //for debugging the debug flag itself lol
- #endif
- char _boolstr[2][6] = { "false", "true\0" };
- #define boolstr(boolvalue) ((boolvalue)?_boolstr[1]:_boolstr[0])
- bool shouldDither = true;
- bool dontCullFrames = false;
- bool paused = false;
- bool aborted = false;
- #define KV3_MAGIC 0x0033564B
- struct FileHeader {
- Uint32 magic; // = 0x0033564B = "KV3\0"
- Uint16 headerSize; // = sizeof(FileHeader)
- Uint32 videoSize;
- Uint32 AudioSize;
- Uint32 w, h; //width and height of video frame
- };
- //
- int _main(int argc, char** argv); //prototype
- int main(int argc, char** argv){
- int returnStatus = -1;
- try {
- if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)<0) throw SDL_GetError();
- returnStatus = _main(argc, argv);
- } catch(const char* errorText){
- Log("Error = \"%s\"", errorText);
- MessageBeep(MB_ICONERROR);
- SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "THE PROGRAM CRASHED!", errorText, nullptr);
- }
- Log("# OF ALLOCATIONS = %u (this should always be 0)", (unsigned)memory::getNumAllocations());
- SDL_Quit(); //it's safe to call this even if SDL_Init fails
- return returnStatus;
- }
- //
- #define USE_SERPENTINE 1
- void dither_formatted_mat(cv::Mat& img){
- if(img.cols*img.rows != TGT_LEN) throw "img is of an incorrect size";
- if(img.type() != CV_8U) throw "img is not of type unsigned char";
- float pixel_old, pixel_new, pixel_err;
- if(!shouldDither){ //use simple threshholding instead of normal dither
- Uint8* pixels = img.ptr<Uint8>();
- for(int i=0; i<TGT_LEN; ++i) pixels[i] = (pixels[i] >= 128) ? 255: 0;
- } else {
- //floyd-steinberg dithering
- img.convertTo(img, CV_32F, 1.0/255);
- float* pixels = img.ptr<float>();
- for(int y=0; y<TGT_Y; ++y){
- #if USE_SERPENTINE == 1
- if((y&1)){ //left-to-right
- for(int x=0; x<TGT_X; ++x){
- pixel_old = pixels[x];
- pixel_new = (pixel_old >= 0.5f) ? 1.0f : 0.0f;
- pixels[x] = pixel_new;
- pixel_err = pixel_old - pixel_new;
- if(x<(TGT_X-1)) pixels[x+1] += pixel_err*(7.0f/16);
- if(y<(TGT_Y-1)){
- if(x>=1 ){ pixels[x+TGT_X-1] += pixel_err*(3.0f/16); }
- { pixels[x+TGT_X ] += pixel_err*(5.0f/16); }
- if(x<(TGT_X-1)){ pixels[x+TGT_X+1] += pixel_err*(1.0f/16); }
- }
- }
- } else { //right-to-left
- for(int x=TGT_X-1; x>=0; --x){
- pixel_old = pixels[x];
- pixel_new = (pixel_old >= 0.5f) ? 1.0f : 0.0f;
- pixels[x] = pixel_new;
- pixel_err = pixel_old - pixel_new;
- if(x>=1) pixels[x-1] += pixel_err*(7.0f/16);
- if(y<(TGT_Y-1)){
- if(x<(TGT_X-1)){ pixels[x+TGT_X+1] += pixel_err*(3.0f/16); }
- { pixels[x+TGT_X ] += pixel_err*(5.0f/16); }
- if(x>=1 ){ pixels[x+TGT_X-1] += pixel_err*(1.0f/16); }
- }
- }
- }
- #else
- for(int x=0; x<TGT_X; ++x){
- pixel_old = pixels[x];
- pixel_new = (pixel_old >= 0.5f) ? 1.0f : 0.0f;
- pixels[x] = pixel_new;
- pixel_err = pixel_old - pixel_new;
- if(x<(TGT_X-1)) pixels[x+1] += pixel_err*(7.0f/16);
- if(y<(TGT_Y-1)){
- if(x>=1 ){ pixels[x+TGT_X-1] += pixel_err*(3.0f/16); }
- { pixels[x+TGT_X ] += pixel_err*(5.0f/16); }
- if(x<(TGT_X-1)){ pixels[x+TGT_X+1] += pixel_err*(1.0f/16); }
- }
- }
- #endif
- pixels += TGT_X;
- }
- img.convertTo(img, CV_8U, 255.0);
- }
- }
- static inline Uint8 encode_char(Uint8* pixel_row, int pixel_column){
- //>>6 so it will return to being 0x80 after an equal number of left shifts
- Uint8 chr = 0x80>>6;
- chr = (chr<<1) | (pixel_row[pixel_column + 1 + TGT_X*2] != 0);
- chr = (chr<<1) | (pixel_row[pixel_column + 0 + TGT_X*2] != 0);
- chr = (chr<<1) | (pixel_row[pixel_column + 1 + TGT_X*1] != 0);
- chr = (chr<<1) | (pixel_row[pixel_column + 0 + TGT_X*1] != 0);
- chr = (chr<<1) | (pixel_row[pixel_column + 1 + TGT_X*0] != 0);
- chr = (chr<<1) | (pixel_row[pixel_column + 0 + TGT_X*0] != 0);
- return chr;
- }
- void write_image_data(cv::Mat& img, File& file){
- Uint8* pixels = img.ptr<Uint8>();
- if(img.cols*img.rows != TGT_LEN) throw "img is of an incorrect size";
- if(img.type() != CV_8U) throw "img is not of type unsigned char";
- if(file.isClosed()) throw "file has already been closed";
- Uint8 row[TGT_X/2];
- for(int y=0; y<TGT_Y; y+=3){
- for(int x=0; x<TGT_X; x+=2)
- row[x>>1] = encode_char(pixels, x);
- file.write(row, sizeof(row));
- pixels += TGT_X*3;
- }
- }
- static inline bool is_image_file(const char* filePath){
- try { return !cv::imread(filePath).empty(); }
- catch(...) { return false; }
- }
- //returns position of '.', or position of
- //original null terminator if no dot was found
- static inline int strip_file_extension(char* filePath){
- char* end = filePath + strnlen(filePath, MAX_PATH);
- char* cur = end;
- while(cur > filePath && *cur != '.') --cur;
- if(cur > filePath) *cur = '\0';
- else cur = end;
- return (int)(cur-filePath);
- }
- //unit should be between 0.0f and 1.0f
- static inline SDL_Color unit_to_ryg(float unit){
- unit = CLAMP(unit*2.0f, 0.0f, 2.0f);
- unsigned char red = 255, green = 255;
- #define RYG_RND(_v) ( (unsigned char)( (_v)+0.5f ) )
- if(unit < 1.0f) green = RYG_RND(255.0f*( unit));
- else red = RYG_RND(255.0f*(2.0f-unit));
- #undef RYG_RND
- SDL_Color color;
- color.r = red;
- color.g = green;
- color.b = 0;
- color.a = 255;
- return color;
- }
- bool handle_events(){ //returns false if program should abort
- bool run = true;
- SDL_Event evt;
- while(SDL_PollEvent(&evt))
- switch(evt.type){
- case SDL_QUIT: run = false; break;
- case SDL_KEYDOWN: if(evt.key.repeat) break;
- {
- paused ^= 1;
- } break;
- }
- return run;
- }
- char _filePathIn[MAX_PATH];
- char filePathOut[MAX_PATH] = "INVALID_FILE_NAME";
- int _main(int argc, char** argv){
- //for(int i=0; i<argc; ++i) Log("%2i: \"%s\"", i, argv[i]);
- //will end up pointing to either _filePathIn or argv[1]
- char* filePathIn = nullptr;
- if(argc < 2){
- OPENFILENAME ofn;
- memory::set(&_filePathIn, 0, sizeof(_filePathIn));
- memory::set(&ofn, 0, sizeof(ofn));
- ofn.lStructSize = sizeof(ofn);
- ofn.hwndOwner = nullptr;
- ofn.lpstrFile = _filePathIn;
- ofn.nMaxFile = sizeof(_filePathIn);
- ofn.lpstrFilter = FILTER_STR;
- ofn.nFilterIndex = 5; //defaults to the .mp4 option
- ofn.lpstrFileTitle = nullptr;
- ofn.nMaxFileTitle = 0;
- ofn.lpstrInitialDir = nullptr;
- ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
- if(GetOpenFileName(&ofn) == TRUE){
- filePathIn = _filePathIn;
- } else {
- DWORD exterr = CommDlgExtendedError();
- if(exterr) Log("CommDlgExtendedError() = 0x%04lX", exterr);
- }
- } else {
- filePathIn = argv[1];
- if(argc >= 3)
- for(int i=2; i<argc; ++i){
- if(argv[i][0] == '-')
- switch(argv[i][1]){
- case 't': shouldDither = false; break; //-t: use threshhold instead of dither
- case 'c': dontCullFrames = true; break; //-c: disable culling of frames to match 10fps
- default:;
- }
- }
- }
- if(filePathIn == nullptr){
- Log("Error: No file was selected!");
- //MessageBeep(MB_ICONWARNING);
- SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Failed to load file!",
- "No file was selected!", nullptr);
- return -1;
- } else if(!fileio::exists(filePathIn)){
- Log("Error: File \"%s\" doesn't exist!", filePathIn);
- MessageBeep(MB_ICONERROR);
- SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Failed to load file!",
- fstr("File \"%s\" doesn't exist!", filePathIn),
- nullptr);
- return -1;
- }
- #ifdef _DEBUG
- #define WIN_X (TGT_X*2)
- #define WIN_Y (TGT_Y*2)
- #else
- #define WIN_X 600
- #define WIN_Y 150
- #endif
- #define PROGRESS_STRING(which_frame) \
- fstr("%04i/%04i, %6.2f%%, %04i written (press any key to pause) %s", \
- (which_frame), (frames), \
- ((float)(which_frame)/(frames))*100.0f, \
- (framesWritten), (paused)?"(PAUSED)":"" )
- SDL_Window* win = SDL_CreateWindow("(LOADING FILE DATA...)",
- SDL_WINDOWPOS_UNDEFINED,
- SDL_WINDOWPOS_UNDEFINED,
- WIN_X, WIN_Y, SDL_WINDOW_RESIZABLE);
- if(!win) throw SDL_GetError();
- SDL_SetWindowMinimumSize(win, WIN_X, WIN_Y);
- SDL_Renderer* rnd = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
- if(!rnd) throw SDL_GetError();
- #ifdef _DEBUG
- SDL_Texture* tex = SDL_CreateTexture(rnd, SDL_PIXELFORMAT_ABGR8888,
- SDL_TEXTUREACCESS_STREAMING,
- TGT_X, TGT_Y);
- if(!tex) throw SDL_GetError();
- #endif
- cv::Mat frame_ori, frame_gray, frame_resized;
- cv::Mat* frame = &frame_resized; //switches to &frame_gray if size is already correct
- Uint64 ticksEnd, ticksStart = SDL_GetTicks64();
- int frames = 1, framesWritten = 0;
- bool is_image = is_image_file(filePathIn);
- //open file and write header
- int dotpos = strip_file_extension(filePathIn);
- strncpy(filePathOut, fstr("%s.kv3", filePathIn), MAX_PATH-1);
- filePathIn[dotpos] = '.'; //put dot back (strip_file_extension sets it to \0)
- File file(filePathOut, "wb");
- //tbd
- //FileHeader hdr;
- //hdr.magic = KV3_MAGIC;
- if(is_image){
- frame_gray = cv::imread(filePathIn, cv::IMREAD_GRAYSCALE);
- if(frame_gray.cols==TGT_X && frame_gray.cols==TGT_Y){
- frame = &frame_gray; //no resize needed
- } else {
- cv::resize(frame_gray, frame_resized, cv::Size(TGT_X, TGT_Y));
- }
- dither_formatted_mat(*frame);
- write_image_data(*frame, file);
- ++framesWritten;
- file.close();
- ticksEnd = SDL_GetTicks64();
- #ifdef _DEBUG
- SDL_SetWindowTitle(win, "Image result");
- Uint8* pixels_src = frame->ptr<Uint8>();
- Uint32* pixels_dst;
- int pitch; //unused
- if(SDL_LockTexture(tex, nullptr, (void**)&pixels_dst, &pitch)<0)
- throw SDL_GetError();
- for(int i=0; i<TGT_LEN; ++i)
- pixels_dst[i] = (pixels_src[i]!=0) ? 0xffffffff : 0xff000000;
- SDL_UnlockTexture(tex);
- while(true){
- if(!handle_events()) break;
- SDL_RenderCopy(rnd, tex, nullptr, nullptr);
- SDL_RenderPresent(rnd);
- SDL_Delay(33);
- }
- #endif
- } else { //video
- cv::VideoCapture video(filePathIn);
- if(!video.isOpened()) throw "Failed to open video file!";
- double fps = video.get(cv::CAP_PROP_FPS );
- frames = (int)video.get(cv::CAP_PROP_FRAME_COUNT ); //(declared earlier)
- int width = (int)video.get(cv::CAP_PROP_FRAME_WIDTH );
- int height = (int)video.get(cv::CAP_PROP_FRAME_HEIGHT);
- #define FRAME_UPDATE_PERIOD 100
- #define FRAMERATE_TARGET 20.0
- double timing_var = 1.0; //so that the 1st frame is guaranteed to be written
- double timing_adv = FRAMERATE_TARGET/fps;
- int waitms_original = (int)(1000.0/fps);
- int frameUpdate = 0;
- SDL_Rect progress_rect;
- memory_set_0(progress_rect);
- progress_rect.h = WIN_Y;
- //SDL_Log instead of Log, so gcc stops complaining about unused vars in release build
- SDL_Log("fps=%.2f, frames=%i, dims=(%i,%i), waitms_original=%i, frameUpdate=%i",
- fps, frames, width, height, waitms_original, frameUpdate);
- if(width==TGT_X && height==TGT_Y) frame = &frame_gray; //&frame_resized otherwise
- for(int f=0; f<frames; ++f){
- if(!handle_events()){ aborted = true; break; }
- if(paused){
- frameUpdate = 0;
- SDL_SetWindowTitle(win, PROGRESS_STRING(f));
- SDL_Delay(33);
- --f; continue;
- }
- #ifdef _DEBUG
- Sint64 ticksFrame = (Sint64)SDL_GetTicks64();
- bool frameRead = false;
- #endif
- if(timing_var < 1.0){ //skip frame
- //(video.grab advances the frame without reading it)
- if(!video.grab()) throw "failed to read frame";
- } else {
- if(!video.read(frame_ori)) throw "failed to read frame";
- cv::cvtColor(frame_ori, frame_gray, cv::COLOR_BGR2GRAY);
- cv::resize(frame_gray, frame_resized, cv::Size(TGT_X, TGT_Y));
- dither_formatted_mat(*frame);
- int how_many_copies_to_write = (int)timing_var; //copies of the same frame
- for(int f2=0; f2<how_many_copies_to_write; ++f2){
- write_image_data(*frame, file);
- ++framesWritten;
- }
- timing_var = SDL_fmod(timing_var, 1.0);
- #ifdef _DEBUG
- frameRead = true;
- #endif
- }
- timing_var += timing_adv;
- #ifdef _DEBUG
- Uint8* pixels_src = frame->ptr<Uint8>();
- Uint32* pixels_dst;
- int pitch; //unused
- if(SDL_LockTexture(tex, nullptr, (void**)&pixels_dst, &pitch)<0)
- throw SDL_GetError();
- for(int i=0; i<TGT_LEN; ++i)
- pixels_dst[i] = (pixels_src[i]!=0) ? 0xffffffff : 0xff000000;
- SDL_UnlockTexture(tex);
- SDL_SetWindowTitle(win, PROGRESS_STRING(f));
- SDL_RenderCopy(rnd, tex, nullptr, nullptr);
- SDL_RenderPresent(rnd);
- #define DELAY_DIFF(ticks) MAX((Sint64)(ticks)-( ((Sint64)SDL_GetTicks64())-ticksFrame ),1)
- if(dontCullFrames) SDL_Delay(DELAY_DIFF(waitms_original));
- else if(frameRead) SDL_Delay(DELAY_DIFF(1000.0/FRAMERATE_TARGET));
- #else
- if(!((frameUpdate++)%FRAME_UPDATE_PERIOD)){
- float unit = (float)f/frames;
- SDL_Color clr = unit_to_ryg(unit);
- progress_rect.w = WIN_X * unit;
- SDL_SetWindowTitle(win, PROGRESS_STRING(f));
- SDL_SetRenderDrawColor(rnd, 0, 0, 0, 255);
- SDL_RenderClear(rnd);
- SDL_SetRenderDrawColor(rnd, clr.r, clr.g, clr.b, clr.a);
- SDL_RenderFillRect(rnd, &progress_rect);
- SDL_RenderPresent(rnd);
- }
- #endif
- }
- file.close();
- ticksEnd = SDL_GetTicks64();
- }
- #ifdef _DEBUG
- SDL_DestroyTexture(tex);
- #endif
- SDL_DestroyRenderer(rnd);
- SDL_DestroyWindow(win);
- if(!aborted){
- //fstr has 2 buffers, so it's safe to do this
- char* title = fstr("Processed %i frame%c (%i written) in %.3f seconds",
- frames, (frames > 1) ? 's' : ' ', framesWritten,
- (float)(ticksEnd-ticksStart)/1000);
- char* message = fstr("Output video saved to \"%s\"\t", filePathOut);
- MessageBeep(MB_ICONINFORMATION);
- SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, title, message, nullptr);
- } else {
- fileio::remove(filePathOut); //remove unfinished file
- SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "!!!",
- "Conversion was aborted!", nullptr);
- }
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement