├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── CPackOptions.txt ├── README.md ├── config.h.in ├── description.txt ├── images ├── 640x425.jpg ├── 640x480.jpg └── Credits.txt └── src ├── CMakeLists.txt └── yuvit.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .project 3 | .cproject 4 | .idea 5 | build/* 6 | *.a 7 | *.a-* 8 | *.dylib 9 | *.dylib-* 10 | *.o 11 | *.o-* 12 | *.dll 13 | *.exe 14 | */Dist/* 15 | _*/ 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | compiler: 4 | - gcc 5 | - clang 6 | 7 | before_install: sudo apt-get update -qq 8 | 9 | install: sudo apt-get install -y libfreeimage-dev 10 | 11 | before_script: 12 | - mkdir build 13 | - cd build 14 | - cmake .. 15 | 16 | script: make 17 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.0) 2 | project(yuvit) 3 | 4 | set(PROJECT_VERSION 0.1.1) 5 | set(PROJECT_URL "https://github.com/stunpix/yuvit") 6 | set(PROJECT_AUTHOR "Alexander Shashkevich ") 7 | set(PROJECT_LICENSE "LGPLv3") 8 | 9 | if(NOT CMAKE_BUILD_TYPE) 10 | set(CMAKE_BUILD_TYPE "Release") 11 | endif() 12 | 13 | # set strict code checking 14 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c1x -Werror -pedantic -Wall -Wextra") 15 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Werror -pedantic -Wall -Wextra") 16 | 17 | find_path(FreeImageIncludeDir NAMES FreeImage.h) 18 | find_library(FreeImageLib freeimage) 19 | 20 | if(FreeImageIncludeDir MATCHES "NOTFOUND" OR FreeImageLib MATCHES "NOTFOUND") 21 | message(FATAL_ERROR "NOT FOUND: FreeImage. 22 | Not installed? Try install: 23 | Debian: sudo apt-get install libfreeimage-dev 24 | RedHat: sudo yum install freeimage-devel 25 | OS X: brew install freeimage") 26 | else() 27 | if(CMAKE_HOST_APPLE) 28 | # On OS X freeimage installed under /usr/local and 29 | # clang doesn't see it, so we must set paths explicitly 30 | get_filename_component(FreeImageLibDir ${FreeImageLib} DIRECTORY) 31 | include_directories(${FreeImageIncludeDir}) 32 | link_directories(${FreeImageLibDir}) 33 | endif() 34 | message(STATUS "FreeImage: FOUND") 35 | endif() 36 | 37 | include_directories(${PROJECT_BINARY_DIR}) 38 | configure_file(config.h.in config.h) 39 | 40 | add_subdirectory(src) 41 | 42 | include(CPackOptions.txt) -------------------------------------------------------------------------------- /CPackOptions.txt: -------------------------------------------------------------------------------- 1 | set(CPACK_GENERATOR DEB RPM) 2 | set(CPACK_PACKAGE_NAME ${PROJECT_NAME}) 3 | set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) 4 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "YUV image and sequence console conversion tool") 5 | set(CPACK_PACKAGE_DESCRIPTION_FILE ${PROJECT_SOURCE_DIR}/description.txt) 6 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_PROCESSOR}") 7 | set(CPACK_STRIP_FILES "ON") 8 | 9 | # DEB package specific options 10 | file(READ ${PROJECT_SOURCE_DIR}/description.txt DEB_DESCRIPTION) 11 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER ${PROJECT_AUTHOR}) 12 | set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${DEB_DESCRIPTION}) 13 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libstdc++6 (>= 4.4.0), libfreeimage3 (>= 3.15.0)") 14 | set(CPACK_DEBIAN_PACKAGE_SECTION "graphics") 15 | set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${PROJECT_URL}) 16 | 17 | # RPM package specific options 18 | set(CPACK_RPM_PACKAGE_LICENSE ${PROJECT_LICENSE}) 19 | set(CPACK_RPM_PACKAGE_URL ${PROJECT_URL}) 20 | set(CPACK_RPM_PACKAGE_REQUIRES "libstdc++, freeimage") 21 | 22 | include(CPack) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | YUVIT 2 | ===== 3 | 4 | [![Build Status](https://travis-ci.org/stunpix/yuvit.svg)](https://travis-ci.org/stunpix/yuvit) 5 | 6 | Command line tool to convert images from popular formats (png, jpeg, etc) into YUV images or even sequences. It can read all image formats which [FreeImage][1] library [supports][2]. 7 | 8 | [1]: http://freeimage.sourceforge.net 9 | [2]: http://freeimage.sourceforge.net/features.html 10 | 11 | Build 12 | ----- 13 | 14 | You'll need cmake, make, g++ and libfreeimage. 15 | 16 | On Debian/Ubuntu: 17 | 18 | sudo apt-get install g++ cmake libfreeimage-dev 19 | 20 | On RadHat/Fedora: 21 | 22 | sudo yum install gcc-c++ cmake freeimage-devel 23 | 24 | For OS X you'll need a [Brew][3] and [Apple Command-line Tools][4]. Install them, then continue: 25 | 26 | [3]: http://brew.sh/ 27 | [4]: https://developer.apple.com/downloads/ 28 | 29 | brew install cmake make freeimage 30 | 31 | Clone sources and build: 32 | 33 | git clone https://github.com/stunpix/yuvit.git 34 | mkdir yuvit/build && cd yuvit/build 35 | cmake .. 36 | make 37 | 38 | For OS X there is only way to install tool using: 39 | 40 | make install 41 | 42 | This installs tool under /usr/local. You can also use this approach for Linux distros, but we recommend to use your system's package manager for installs. To get .deb and .rpm packages, run: 43 | 44 | cpack 45 | 46 | and install package using your package manager. With this approach your system will be not polluted with files that aren't controlled by package manager. 47 | 48 | Usage 49 | ----- 50 | 51 | Usage: yuvit [options] 52 | 53 | Options: 54 | -h 55 | This help 56 | -o 57 | Output filename. Could be a pattern for read multifile sequences. 58 | -a 59 | Append mode. Images will be append to output file. Doesn't truncate output file. 60 | -m : 61 | Start and end numbers for multifile sequences. 62 | -i 63 | Interleave UV rows for planar formats 64 | -w 65 | Swap UV components order 66 | -x 67 | Use YUV conversion matrix. Default: jpeg 68 | jpeg 69 | JFIF specification matrix: 70 | |Y| | 0.299 0.587 0.114| |R| 71 | |U| = |-0.168736 -0.331264 0.5 | x |G| 72 | |V| | 0.5 -0.418688 -0.081| |B| 73 | sdtv 74 | ITU-R BT.601 for SDTV specification matrix: 75 | |Y| | 0.299 0.587 0.114 | |R| 76 | |U| = |-0.14713 -0.28886 0.436 | x |G| 77 | |V| | 0.615 -0.51499 -0.10001| |B| 78 | hdtv 79 | ITU-R BT.709 for HDTV specification matrix: 80 | |Y| | 0.2126 0.7152 0.0722 | |R| 81 | |U| = |-0.09991 -0.33609 0.436 | x |G| 82 | |V| | 0.615 -0.55861 -0.05639| |B| 83 | 84 | -f 85 | Output YUV format. Default: yuv" 86 | yuv 87 | Planar format 88 | yuyv 89 | Packed format 90 | uyvy 91 | Packed format 92 | yyuv 93 | Planar luma with packed chroma format 94 | -s 95 | Chroma scaling. Used only for planar formats. Default: h1v1 96 | h1v1 97 | UV not scaled down [DEFAULT] 98 | h2v2 99 | UV scaled down by 2x horizontally and vertically 100 | h2v1 101 | UV scaled down by 2x horizontally 102 | h1v2 103 | UV scaled down by 2x vertically 104 | Multifile sequences: 105 | Use '#' in file names, so they will be replaced with numbers. 106 | Examples: 107 | yuvit -a -m 0:100 -o out.yuv test###.bmp 108 | Converts: 'test000.bmp'...'test100.bmp' -> 'out.yuv' 109 | yuvit -m 10:200 -o out###.yuv test######.jpg 110 | Converts: 'test000010.jpg'...'test000200.jpg' -> 'out010.yuv'...'out200.yuv' 111 | 112 | Examples 113 | -------- 114 | 115 | Options to convert to popular YUV formats. All examples show YUV layout of resulting 4x4 image. 116 | 117 | ### Planar ### 118 | 119 | **I420, IYUV** : -f yuv -s h2v2 120 | 121 | YYYY 122 | YYYY 123 | YYYY 124 | YYYY 125 | UU 126 | UU 127 | VV 128 | VV 129 | 130 | **YV12 (MPEG codecs)** : -f yuv -s h2v2 -w 131 | 132 | YYYY 133 | YYYY 134 | YYYY 135 | YYYY 136 | VV 137 | VV 138 | UU 139 | UU 140 | 141 | **YV16** : -f yuv -s h2v1 -w 142 | 143 | YY 144 | YY 145 | V 146 | V 147 | U 148 | U 149 | 150 | **NV12** : -f yyuv -s h2v2 151 | 152 | YYYY 153 | YYYY 154 | YYYY 155 | YYYY 156 | UVUV 157 | UVUV 158 | 159 | ### Packed ### 160 | 161 | **YUY2, V422, YUYV** : -f yuyv 162 | 163 | YUYVYUYV 164 | YUYVYUYV 165 | YUYVYUYV 166 | YUYVYUYV 167 | 168 | **YVYU** : -f yuyv -w 169 | 170 | YVYUYVYU 171 | YVYUYVYU 172 | YVYUYVYU 173 | YVYUYVYU 174 | 175 | **UYVY** : -f uyvy 176 | 177 | UYVYUYVY 178 | UYVYUYVY 179 | UYVYUYVY 180 | UYVYUYVY 181 | 182 | Author 183 | ------ 184 | 185 | 2005-2015 Alexander Shashkevich 186 | 187 | License 188 | ------- 189 | 190 | Source code is licensed under LGPLv3. 191 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | #define PROJECT_VERSION "@PROJECT_VERSION@" 2 | #define PROJECT_AUTHOR "@PROJECT_AUTHOR@" 3 | #define PROJECT_URL "@PROJECT_URL@" 4 | #define PROJECT_LICENSE "@PROJECT_LICENSE@" -------------------------------------------------------------------------------- /description.txt: -------------------------------------------------------------------------------- 1 | Console tool for converting single or sequenced YUV images from/to PNG, TIFF, JPEG, BMP, RAW and many others supported by FreeImage library. Tool supports wide range of YUV formats including some rare ones. -------------------------------------------------------------------------------- /images/640x425.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frkd-dev/yuvit/427aa05a23dfbc426ef16873e2f564b62536eda9/images/640x425.jpg -------------------------------------------------------------------------------- /images/640x480.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frkd-dev/yuvit/427aa05a23dfbc426ef16873e2f564b62536eda9/images/640x480.jpg -------------------------------------------------------------------------------- /images/Credits.txt: -------------------------------------------------------------------------------- 1 | 640x480.jpg 2 | Author: Dan Lundberg 3 | License: CC-BY-SA 2.0 4 | URL: https://www.flickr.com/photos/9508280@N07/13540627983/ 5 | 6 | 640x425.jpg 7 | Author: Dan Lundberg 8 | License: CC-BY-SA 2.0 9 | URL: https://www.flickr.com/photos/9508280@N07/13540621323/ 10 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES yuvit.cpp) 2 | 3 | add_executable(yuvit ${SOURCES}) 4 | target_link_libraries(yuvit freeimage) 5 | install(TARGETS yuvit 6 | RUNTIME DESTINATION bin 7 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE 8 | ) -------------------------------------------------------------------------------- /src/yuvit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | 12 | #define LOG_MESSAGE(...) {printf(__VA_ARGS__); printf("\n");} 13 | #define LOG_ERROR(...) {printf("ERROR: "); printf(__VA_ARGS__); printf("\n");} 14 | 15 | enum YUVFormat 16 | { 17 | YUV_YUV, 18 | YUV_UYVY, 19 | YUV_YUYV, 20 | YUV_YYUV 21 | }; 22 | 23 | enum YUVScale 24 | { 25 | SCALE_H2V2, 26 | SCALE_H2V1, 27 | SCALE_H1V2, 28 | SCALE_H1V1 29 | }; 30 | 31 | class Config 32 | { 33 | public: 34 | bool appendMode; /* if not zero append YUV image(s) to output file */ 35 | bool uvInterleave; /* if not zero, UV rows in planar images are interleaved*/ 36 | bool uvOrderSwap; /**/ 37 | uint32_t yuvFormat; /* YUV output mode. Default: h2v2 */ 38 | uint32_t uvScale; /* Defines how UV components are scaled in planar mode */ 39 | unsigned long seqStart; /* Sequence start for multiple files */ 40 | unsigned long seqEnd; /* Sequence end for multiple files */ 41 | string inFileNamePattern; 42 | string outFileNamePattern; 43 | vectoryuvMatrix; 44 | 45 | Config(); 46 | 47 | // Returns "false" on errors and "true" if no errors 48 | bool ParseArgs(char* args[], int count); 49 | void LoadJPEGMatrix(); 50 | void LoadSDTVMatrix(); 51 | void LoadHDTVMatrix(); 52 | 53 | private: 54 | bool ParseSequenceRange(string range); 55 | }; 56 | 57 | void PrintHelp(); 58 | string ExpandPattern(string pattern, unsigned long counter); 59 | 60 | int main(int argc, char* argv[]) 61 | { 62 | Config cfg; 63 | string inFileName, outFileName; 64 | FILE* hOutFile = 0; 65 | uint8_t errorFlag = 1; // By default we will exiting with error 66 | FIBITMAP *inImage = 0; 67 | uint32_t bpp = 0; 68 | uint32_t lumaWidth, lumaHeight; 69 | uint32_t chromaWidth, chromaHeight; 70 | uint32_t x, y, xMask, yMask; 71 | uint8_t Rc, Gc, Bc; 72 | uint8_t *rgbPixels; 73 | uint8_t *yPixels, *uPixels, *vPixels; 74 | uint8_t *yPtr, *uPtr, *vPtr; 75 | bool warned = false; 76 | 77 | if(!cfg.ParseArgs(argv, argc)) 78 | exit(1); 79 | 80 | FreeImage_Initialise(); 81 | 82 | LOG_MESSAGE("Processing:"); 83 | 84 | /* First expanding of output filename. If append mode - this is only one place 85 | where name is expanded */ 86 | outFileName = ExpandPattern(cfg.outFileNamePattern, cfg.seqStart); 87 | 88 | // For performance reasons get matrix values here to put them on stack 89 | // instead of accessing them in deep loops from vector 90 | float yr = cfg.yuvMatrix[0], yg = cfg.yuvMatrix[1], yb = cfg.yuvMatrix[2]; 91 | float ur = cfg.yuvMatrix[3], ug = cfg.yuvMatrix[4], ub = cfg.yuvMatrix[5]; 92 | float vr = cfg.yuvMatrix[6], vg = cfg.yuvMatrix[7], vb = cfg.yuvMatrix[8]; 93 | 94 | /* Main loop of passing through all images */ 95 | while(cfg.seqStart <= cfg.seqEnd) 96 | { 97 | inFileName = ExpandPattern(cfg.inFileNamePattern, cfg.seqStart); 98 | 99 | FREE_IMAGE_FORMAT format = FreeImage_GetFileType(inFileName.c_str()); 100 | if(format == FIF_UNKNOWN) 101 | { 102 | LOG_ERROR("Input image format is unknown or unsupported..."); 103 | goto HandleError; 104 | } 105 | 106 | inImage = FreeImage_Load(format, inFileName.c_str()); 107 | 108 | if(inImage == 0) 109 | { 110 | LOG_ERROR("Some problem occurred with reading of input image..."); 111 | goto HandleError; 112 | } 113 | 114 | if( !cfg.appendMode ) 115 | outFileName = ExpandPattern(cfg.outFileNamePattern, cfg.seqStart); 116 | 117 | hOutFile = fopen( outFileName.c_str(), cfg.appendMode ? "ab" : "wb" ); 118 | 119 | if(hOutFile == 0) 120 | { 121 | LOG_ERROR("Can not open output file..."); 122 | goto HandleError; 123 | } 124 | 125 | bpp = FreeImage_GetBPP(inImage); 126 | lumaWidth = FreeImage_GetWidth(inImage); 127 | lumaHeight = FreeImage_GetHeight(inImage); 128 | 129 | LOG_MESSAGE("\t%s [%dx%d %ubpp]", inFileName.c_str(), lumaWidth, lumaHeight, bpp); 130 | 131 | if( (lumaWidth & 1) && cfg.yuvFormat != YUV_YUV) 132 | { 133 | LOG_ERROR("Width of source image is odd - this is incompatible with packed format..."); 134 | goto HandleError; 135 | } 136 | 137 | if( !warned && ((lumaWidth & 1) || (lumaHeight & 1)) && (cfg.yuvFormat == YUV_YUV || cfg.yuvFormat == YUV_YYUV)) 138 | { 139 | LOG_MESSAGE("Warning! Dimensions of the source image are odd. This may cause incompatibility with some YUV viewers."); 140 | warned = true; // Show warning only once 141 | } 142 | 143 | if (bpp != 24) 144 | { 145 | FIBITMAP *newBitmap = FreeImage_ConvertTo24Bits(inImage); 146 | 147 | if (newBitmap == NULL) 148 | { 149 | LOG_ERROR("Failed to convert image to 24bpp"); 150 | goto HandleError; 151 | } 152 | 153 | FreeImage_Unload(inImage); 154 | inImage = newBitmap; 155 | } 156 | 157 | FreeImage_FlipVertical(inImage); 158 | 159 | /* Calculate dimensions of destination UV components */ 160 | switch(cfg.uvScale) 161 | { 162 | default: /* Default scale h1v1 */ 163 | case SCALE_H1V1: 164 | chromaWidth = lumaWidth; 165 | chromaHeight = lumaHeight; 166 | xMask = 0; 167 | yMask = 0; 168 | break; 169 | case SCALE_H2V2: 170 | chromaWidth = lumaWidth / 2; 171 | chromaHeight = lumaHeight / 2; 172 | yMask = xMask = 1; 173 | break; 174 | case SCALE_H1V2: 175 | chromaWidth = lumaWidth; 176 | chromaHeight = lumaHeight / 2; 177 | xMask = 0; 178 | yMask = 1; 179 | break; 180 | case SCALE_H2V1: 181 | chromaWidth = lumaWidth / 2; 182 | chromaHeight = lumaHeight; 183 | xMask = 1; 184 | yMask = 0; 185 | break; 186 | } 187 | 188 | // Pointers that are always pointing on buffers 189 | yPixels = new uint8_t[lumaHeight * lumaWidth]; 190 | uPixels = new uint8_t[chromaHeight * chromaWidth]; 191 | vPixels = new uint8_t[chromaHeight * chromaWidth]; 192 | 193 | // Pointers we are working with 194 | yPtr = yPixels; 195 | uPtr = uPixels; 196 | vPtr = vPixels; 197 | 198 | /* Main converting cycle */ 199 | for(y = 0; y < lumaHeight; y++) 200 | { 201 | rgbPixels = FreeImage_GetScanLine(inImage, y); 202 | 203 | for(x = 0; x < lumaWidth; x++) 204 | { 205 | Rc = *rgbPixels++; 206 | Gc = *rgbPixels++; 207 | Bc = *rgbPixels++; 208 | 209 | *yPtr++ = uint8_t(Rc * yr + Gc * yg + Bc * yb); 210 | if((y & yMask) == 0 && (x & xMask) == 0 && (y / 2) < chromaHeight && (x / 2) < chromaWidth) 211 | { 212 | *uPtr++ = uint8_t(Rc * ur + Gc * ug + Bc * ub + 128); 213 | *vPtr++ = uint8_t(Rc * vr + Gc * vg + Bc * vb + 128); 214 | } 215 | } 216 | } 217 | 218 | if(cfg.uvOrderSwap) 219 | { // UV components should be swapped, so just swap pointers 220 | cout << "Swapping UV order" << endl; 221 | uint8_t* tmp = uPixels; 222 | uPixels = vPixels; 223 | vPixels = tmp; 224 | } 225 | 226 | yPtr = yPixels; 227 | uPtr = uPixels; 228 | vPtr = vPixels; 229 | 230 | if(cfg.yuvFormat == YUV_YUV) 231 | { // Writing planar image 232 | fwrite(yPixels, 1, lumaWidth * lumaHeight, hOutFile); 233 | 234 | if(cfg.uvInterleave) 235 | { // U and V rows should be interleaved after each other 236 | while(chromaHeight--) 237 | { 238 | fwrite(uPtr, 1, chromaWidth, hOutFile); // Write U line 239 | fwrite(vPtr, 1, chromaWidth, hOutFile); // Write V line 240 | uPtr += chromaWidth; 241 | vPtr += chromaWidth; 242 | } 243 | }else{ 244 | // Simply write U and V planes 245 | fwrite(uPixels, 1, chromaWidth * chromaHeight, hOutFile); 246 | fwrite(vPixels, 1, chromaWidth * chromaHeight, hOutFile); 247 | } 248 | }else if(cfg.yuvFormat == YUV_YYUV) 249 | { // Writing planar image 250 | fwrite(yPixels, 1, lumaWidth * lumaHeight, hOutFile); 251 | 252 | // U and V columns should be interleaved after each other 253 | for(uint32_t row = 0; row < chromaHeight; row++) 254 | { 255 | for(uint32_t col = 0; col < chromaWidth; col++) 256 | { // Write in following order Y, U, Y, V 257 | fwrite(uPtr++, 1, 1, hOutFile); 258 | fwrite(vPtr++, 1, 1, hOutFile); 259 | } 260 | } 261 | }else{ 262 | // Writing packed image 263 | if(cfg.yuvFormat == YUV_YUYV) 264 | { 265 | for(uint32_t row = 0; row < lumaHeight; row++) 266 | { 267 | for(uint32_t col = 0; col < lumaWidth; col += 2) 268 | { // Write in following order Y, U, Y, V 269 | fwrite(yPtr++, 1, 1, hOutFile); 270 | fwrite(uPtr++, 1, 1, hOutFile); 271 | fwrite(yPtr++, 1, 1, hOutFile); 272 | fwrite(vPtr++, 1, 1, hOutFile); 273 | } 274 | } 275 | }else{ 276 | for(uint32_t row = 0; row < lumaHeight; row++) 277 | { 278 | for(uint32_t col = 0; col < lumaWidth; col += 2) 279 | { // Write in following order U, Y, V, Y 280 | fwrite(uPtr++, 1, 1, hOutFile); 281 | fwrite(yPtr++, 1, 1, hOutFile); 282 | fwrite(vPtr++, 1, 1, hOutFile); 283 | fwrite(yPtr++, 1, 1, hOutFile); 284 | } 285 | } 286 | } 287 | } 288 | 289 | FreeImage_Unload(inImage); 290 | inImage = 0; 291 | 292 | fclose(hOutFile); 293 | hOutFile = 0; 294 | 295 | delete[] yPixels; 296 | delete[] uPixels; 297 | delete[] vPixels; 298 | 299 | cfg.seqStart++; 300 | } 301 | 302 | LOG_MESSAGE("Done!"); 303 | 304 | errorFlag = 0; // We successful passed all stages, so set flag to zero which means - OK 305 | 306 | HandleError: 307 | if(inImage) 308 | FreeImage_Unload(inImage); 309 | 310 | if(hOutFile) 311 | fclose(hOutFile); 312 | 313 | FreeImage_DeInitialise(); 314 | 315 | return errorFlag; 316 | } 317 | 318 | string toString(unsigned long value) 319 | { 320 | ostringstream oss; 321 | oss << value; 322 | return oss.str(); 323 | } 324 | 325 | /* 326 | Description: Scan for '#' symbols and replaces them with integer value (and leading zeros if needed) 327 | Returns: Formed string 328 | */ 329 | 330 | string ExpandPattern(string pattern, unsigned long counter) 331 | { 332 | string result; 333 | string::iterator it = pattern.begin(); 334 | uint32_t cntr = 0; 335 | string counterStr = toString(counter); 336 | 337 | // Copy from input pattern to resulting string until we meet '#' sign 338 | while( it != pattern.end() && *it != '#') result += *it++; 339 | 340 | // Calculate number of '#' signs in input pattern 341 | while( it != pattern.end() && *it == '#') 342 | { 343 | it++; 344 | cntr++; 345 | } 346 | 347 | // If we not found any patterns - just return unmodified input pattern 348 | if( !cntr ) 349 | return pattern; 350 | 351 | // Determine leading zeros 352 | if(cntr > counterStr.length()) 353 | { 354 | // We have leading zeros 355 | cntr -= counterStr.length(); 356 | }else{ 357 | // Resulting counter bigger than pattern has defined, 358 | // therefore there are no leading zeros 359 | cntr = 0; 360 | } 361 | // cntr from now contains number of leading zeros 362 | 363 | // Append to result leading zeros 364 | if(cntr) 365 | result.append(cntr, '0'); 366 | 367 | // Append integer value 368 | result.append(counterStr); 369 | 370 | // Copy from input pattern to resulting string rest of input pattern 371 | while( it != pattern.end() ) result += *it++; 372 | 373 | return result; 374 | } 375 | 376 | Config::Config() 377 | { 378 | appendMode = false; /* if not zero append YUV image(s) to output file */ 379 | uvOrderSwap = false; /* no UV order swap */ 380 | uvInterleave = false; /* no UV interleaving */ 381 | yuvFormat = YUV_YUV; /* YUV output mode. Default: h2v2 */ 382 | uvScale = SCALE_H1V1; /* UV scaling for planar mode. Default: h1v1 */ 383 | seqStart = 0; /* Sequence start for multiple files */ 384 | seqEnd = 0; /* Sequence end for multiple files */ 385 | LoadJPEGMatrix(); 386 | } 387 | 388 | void Config::LoadJPEGMatrix() 389 | { 390 | yuvMatrix = { 0.299, 0.587, 0.114, 391 | -0.168736, -0.331264, 0.5, 392 | 0.5, -0.418688, -0.081312}; 393 | } 394 | 395 | void Config::LoadSDTVMatrix() 396 | { 397 | yuvMatrix = { 0.299, 0.587, 0.114, 398 | -0.14713, -0.28886, 0.436, 399 | 0.615, -0.51499, -0.10001}; 400 | } 401 | 402 | void Config::LoadHDTVMatrix() 403 | { 404 | yuvMatrix = { 0.2126, 0.7152, 0.0722, 405 | -0.09991, -0.33609, 0.436, 406 | 0.615, -0.55861, -0.05639}; 407 | } 408 | 409 | bool Config::ParseArgs(char* args[], int count) 410 | { 411 | string seqRangeOption; 412 | string yuvFormatOption; 413 | string uvScaleOption; 414 | string matrixOption; 415 | bool error = true; 416 | 417 | while(true) { 418 | int c = getopt(count, args, "haiwm:f:s:o:x:"); 419 | 420 | if (c == -1) 421 | break; 422 | 423 | switch (c) { 424 | case 'h': 425 | goto HandleError; 426 | case 'a': 427 | appendMode = true; 428 | break; 429 | case 'i': 430 | uvInterleave = true; 431 | break; 432 | case 'w': 433 | uvOrderSwap = true; 434 | break; 435 | case 'm': 436 | seqRangeOption = optarg; 437 | break; 438 | case 'f': 439 | yuvFormatOption = optarg; 440 | break; 441 | case 's': 442 | uvScaleOption = optarg; 443 | break; 444 | case 'o': 445 | outFileNamePattern = optarg; 446 | break; 447 | case 'x': 448 | matrixOption = optarg; 449 | break; 450 | default: 451 | cerr << "Unknown option: '" << (char)optopt << "'" << endl; 452 | break; 453 | }; 454 | } 455 | 456 | if (optind != count) 457 | { 458 | inFileNamePattern = args[optind]; 459 | } else { 460 | cerr << "Input file not specified" << endl; 461 | goto HandleError; 462 | } 463 | 464 | if (outFileNamePattern.length() == 0) 465 | outFileNamePattern = inFileNamePattern + ".yuv"; 466 | 467 | // Scaling could be overridden by format, then select scale first 468 | if( uvScaleOption == "h2v2") 469 | uvScale = SCALE_H2V2; 470 | else if(uvScaleOption == "h1v1") 471 | uvScale = SCALE_H1V1; 472 | else if(uvScaleOption == "h1v2") 473 | uvScale = SCALE_H1V2; 474 | else if(uvScaleOption == "h2v1") 475 | uvScale = SCALE_H2V1; 476 | else if( !uvScaleOption.empty()) 477 | { 478 | LOG_ERROR("Unknown UV scaling..."); 479 | goto HandleError; 480 | } 481 | 482 | if( yuvFormatOption == "yuv") 483 | yuvFormat = YUV_YUV; 484 | else if(yuvFormatOption == "yyuv") 485 | yuvFormat = YUV_YYUV; 486 | else if(yuvFormatOption == "yuyv") 487 | yuvFormat = YUV_YUYV, uvScale = SCALE_H2V1; // Packed format always h2v1 488 | else if(yuvFormatOption == "uyvy") 489 | yuvFormat = YUV_UYVY, uvScale = SCALE_H2V1; // Packed format always h2v1 490 | else if( !yuvFormatOption.empty()) 491 | { 492 | LOG_ERROR("Unknown YUV format..."); 493 | goto HandleError; 494 | } 495 | 496 | if(!seqRangeOption.empty()) 497 | if(!ParseSequenceRange(seqRangeOption)) 498 | { 499 | LOG_ERROR("You've specified bad sequence range..."); 500 | goto HandleError; 501 | } 502 | 503 | if (!matrixOption.empty()) 504 | { 505 | if (matrixOption == "jpeg") 506 | LoadJPEGMatrix(); 507 | else if (matrixOption == "sdtv") 508 | LoadSDTVMatrix(); 509 | else if (matrixOption == "hdtv") 510 | LoadHDTVMatrix(); 511 | else { 512 | LOG_ERROR("Unknown matrix option '%s'", matrixOption.c_str()); 513 | goto HandleError; 514 | } 515 | } 516 | 517 | error = false; 518 | HandleError: 519 | 520 | return !error; 521 | } 522 | 523 | bool Config::ParseSequenceRange(string range) 524 | { 525 | string::iterator it = range.begin(); 526 | string seqStartOpt; 527 | string seqEndOpt; 528 | char *end = 0; 529 | 530 | // Copy from input to seqStartOpt until we will find ':' character 531 | while(it != range.end() && *it != ':') 532 | seqStartOpt += *it++; 533 | 534 | if(it == range.end() || *it++ != ':') 535 | return false; 536 | 537 | // Copy from input to seqEndOpt till the end 538 | while(it != range.end()) 539 | seqEndOpt += *it++; 540 | 541 | if(seqStartOpt.empty() || seqEndOpt.empty()) 542 | return false; 543 | 544 | seqStart = strtoul(seqStartOpt.c_str(), &end, 10); 545 | seqEnd = strtoul(seqEndOpt.c_str(), &end, 10); 546 | 547 | return true; 548 | } 549 | 550 | void PrintHelp() 551 | { 552 | cout << 553 | "\nUsage: yuvit [options] \n\n" 554 | "Options:\n" 555 | " -h\n" 556 | " This help\n" 557 | " -o\n" 558 | " Output filename. Could be a pattern for read multifile sequences.\n" 559 | " -a\n" 560 | " Append mode. Images will be append to output file. Doesn't truncate output file.\n" 561 | " -m :\n" 562 | " Start and end numbers for multifile sequences.\n" 563 | " -i\n" 564 | " Interleave UV rows for planar formats\n" 565 | " -w\n" 566 | " Swap UV components order\n" 567 | " -x \n" 568 | " Use YUV conversion matrix. Default: jpeg\n" 569 | " jpeg\n" 570 | " JFIF specification matrix:\n" 571 | " |Y| | 0.299 0.587 0.114| |R|\n" 572 | " |U| = |-0.168736 -0.331264 0.5 | x |G|\n" 573 | " |V| | 0.5 -0.418688 -0.081| |B|\n" 574 | " sdtv\n" 575 | " ITU-R BT.601 for SDTV specification matrix:\n" 576 | " |Y| | 0.299 0.587 0.114 | |R|\n" 577 | " |U| = |-0.14713 -0.28886 0.436 | x |G|\n" 578 | " |V| | 0.615 -0.51499 -0.10001| |B|\n" 579 | " hdtv\n" 580 | " ITU-R BT.709 for HDTV specification matrix:\n" 581 | " |Y| | 0.2126 0.7152 0.0722 | |R|\n" 582 | " |U| = |-0.09991 -0.33609 0.436 | x |G|\n" 583 | " |V| | 0.615 -0.55861 -0.05639| |B|\n" 584 | 585 | " -f \n" 586 | " Output YUV format. Default: yuv" 587 | " yuv\n" 588 | " Planar format\n" 589 | " yuyv\n" 590 | " Packed format\n" 591 | " uyvy\n" 592 | " Packed format\n" 593 | " yyuv\n" 594 | " Planar packed chroma format\n" 595 | " -s \n" 596 | " Chroma scaling. Used only for planar formats. Default: h1v1\n" 597 | " h1v1\n" 598 | " UV not scaled down [DEFAULT]\n" 599 | " h2v2\n" 600 | " UV scaled down by 2x horizontally and vertically\n" 601 | " h2v1\n" 602 | " UV scaled down by 2x horizontally\n" 603 | " h1v2\n" 604 | " UV scaled down by 2x vertically\n" 605 | "\nMultifile sequences:\n" 606 | " Use '#' in file names, so they will be replaced with numbers.\n" 607 | " Examples:\n" 608 | " yuvit -a -m 0:100 -o out.yuv test###.bmp\n" 609 | " Converts: 'test000.bmp'...'test100.bmp' -> 'out.yuv'\n" 610 | " yuvit -m 10:200 -o out###.yuv test######.jpg\n" 611 | " Converts: 'test000010.jpg'...'test000200.jpg' -> 'out010.yuv'...'out200.yuv'\n" 612 | "\nYUV converter v" PROJECT_VERSION "\n" 613 | "by " PROJECT_AUTHOR "\n" 614 | "License: " PROJECT_LICENSE "\n" 615 | "URL: " PROJECT_URL "\n" 616 | ; 617 | } 618 | --------------------------------------------------------------------------------