pfsinyuv.cpp (pfstools-2.1.0.tgz) | : | pfsinyuv.cpp (pfstools-2.2.0.tgz) | ||
---|---|---|---|---|
/** | /** | |||
az @brief Read RAW Yuv files, commonly used for video compression | * @brief Read RAW Yuv files, commonly used for video compression | |||
* | * | |||
* This file is a part of PFSTOOLS package. | * This file is a part of PFSTOOLS package. | |||
* ---------------------------------------------------------------------- | * ---------------------------------------------------------------------- | |||
* Copyright (C) 2017 Rafal Mantiuk | * Copyright (C) 2017 Rafal Mantiuk | |||
* | * | |||
* This program is free software; you can redistribute it and/or modify | * This program is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | * the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | * (at your option) any later version. | |||
* | * | |||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | * GNU General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | |||
* along with this program; if not, write to the Free Software | * along with this program; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
* ---------------------------------------------------------------------- | * ---------------------------------------------------------------------- | |||
* | * | |||
* @author Rafal Mantiuk, <mantiuk@mpi-sb.mpg.de> | * @author George Ash | |||
* | * | |||
* $Id: pfsinrgbe.cpp,v 1.5 2014/06/17 21:57:08 rafm Exp $ | ||||
*/ | */ | |||
#include <config.h> | #include <config.h> | |||
#include <string.h> | #include <string.h> | |||
#include <cstdlib> | #include <cstdlib> | |||
#include <algorithm> | #include <algorithm> | |||
#include <iostream> | #include <iostream> | |||
#include <vector> | #include <vector> | |||
//#include <arpa/inet.h> | ||||
#include <cmath> | ||||
#include <getopt.h> | #include <getopt.h> | |||
#include <pfs.h> | #include <pfs.h> | |||
#include <pfsutils.cpp> | #include <pfsutils.cpp> | |||
#include <climits> | #include <climits> | |||
#define PROG_NAME "pfsinyuv" | #define PROG_NAME "pfsinyuv" | |||
char charsToRemove[7] = {'b', 'i', 't', 's', 'f', 'p', 's'}; | char charsToRemove[7] = {'b', 'i', 't', 's', 'f', 'p', 's'}; | |||
//PL means a planar format per frame. | ||||
enum ChromaFormat {PL420, PL444, V210}; | ||||
template<typename T> | template<typename T> | |||
inline T clamp( T val, T min, T max ) | inline T clamp( T val, T min, T max ) | |||
{ | { | |||
if( val < min ) | if( val < min ) | |||
return min; | return min; | |||
if( val > max ) | if( val > max ) | |||
return max; | return max; | |||
return val; | return val; | |||
} | } | |||
skipping to change at line 81 | skipping to change at line 85 | |||
for( int x = 0; x < width; x++ ) { | for( int x = 0; x < width; x++ ) { | |||
float v_float = (float)line_buf[x]*gain - offset; | float v_float = (float)line_buf[x]*gain - offset; | |||
(*dest)(x<<stride, y<<stride) = clamp( v_float, min, max ); | (*dest)(x<<stride, y<<stride) = clamp( v_float, min, max ); | |||
} | } | |||
} | } | |||
delete [] line_buf; | delete [] line_buf; | |||
return true; | return true; | |||
} | } | |||
void upscale_420to444( pfs::Array2D *ch ) | ||||
{ | ||||
const int height = ch->getRows(); | ||||
const int width = ch->getCols(); | ||||
// For even rows | ||||
for( int y = 0; y < height; y += 2 ) { | ||||
float v_l = (*ch)(0,y); | ||||
float v_r; | ||||
int x; | ||||
for( x = 1; x < (width-1); x += 2 ) { | ||||
v_r = (*ch)(x+1,y); | ||||
// Interpolate between pixels on the left and right | ||||
(*ch)(x,y) = (v_l + v_r)/2.f; | ||||
v_l = v_r; | ||||
} | ||||
if( x < width ) | ||||
(*ch)(x,y) = v_r; | ||||
} | ||||
// For odd rows | ||||
int y; | ||||
for( y = 1; y < (height-1); y += 2 ) { | ||||
for( int x = 0; x < width; x++ ) { | ||||
(*ch)(x,y) = ((*ch)(x,y-1) + (*ch)(x,y+1))/2.f; | ||||
} | ||||
} | ||||
// Last row | ||||
if( y < height ) { | ||||
for( int x = 0; x < width; x++ ) { | ||||
(*ch)(x,y) = (*ch)(x,y-1); | ||||
} | ||||
} | ||||
} | ||||
void upscale_422to444( pfs::Array2D *ch ) | ||||
{ | ||||
const int height = ch->getRows(); | ||||
const int width = ch->getCols(); | ||||
// For odd columns | ||||
for (int y = 0; y < height; y++){ | ||||
for( int x = 1; x < width-1; x+=2 ) { | ||||
(*ch)(x,y) = ((*ch)(x-1,y) + (*ch)(x+1,y))/2.f; | ||||
} | ||||
} | ||||
} | ||||
void parse210Block(unsigned int word, pfs::Array2D *thirdPart, pfs::Array2D *sec | ||||
ondPart, pfs::Array2D *firstPart, unsigned int thirdPartX, unsigned int secondPa | ||||
rtx, unsigned int firstPartX, unsigned int y, bool ycy){ //last bool denotes the | ||||
ordering of luma and chroma within a word. not ycy -? cyc | ||||
const unsigned int bitmask = 0x03FF; | ||||
const float luma_offset = 16.f/219.f; | ||||
const float luma_gain = 1.f/( (float)(1<<(2))*219.f); | ||||
const float chroma_offset = 128.f/224.f; | ||||
const float chroma_gain = 1.f/( (float)(1<<(2))*224.f); | ||||
//now because of the somewhat arbitrary ordering, we don't know beforehand wha | ||||
t offset and gain to apply. I'm just trying to make this less messy, but there's | ||||
definitely a better way. TODO this. | ||||
float first_last_gain = ycy ? luma_gain : chroma_gain; | ||||
float first_last_offset = ycy ? luma_offset : chroma_offset; | ||||
float second_gain = ycy ? chroma_gain : luma_gain; | ||||
float second_offset = ycy ? chroma_offset : luma_offset; | ||||
(*thirdPart)(thirdPartX,y) = (float)((word&(bitmask<<20))>>20)*first_last_gain | ||||
- first_last_offset; | ||||
(*secondPart)(secondPartx,y) = (float)((word&(bitmask<<10))>>10)*second_gain - | ||||
second_offset; | ||||
(*firstPart)(firstPartX,y) = (float)(word&(bitmask))*first_last_gain - first_l | ||||
ast_offset; | ||||
} | ||||
bool read_v210_yuv( FILE *fh, int width, int height, pfs::Array2D *Ydest,pfs::Ar | ||||
ray2D *Cbdest, pfs::Array2D *Crdest){ | ||||
assert(sizeof(unsigned int) == 4); | ||||
//2*width needed because width is needed to store luma, and width is also need | ||||
ed to store both chroma | ||||
unsigned int paddedLineWidth = (unsigned int) ceil(((float)width)/6.)*4; //in | ||||
32 bit words | ||||
unsigned int * line_buf = new unsigned int[paddedLineWidth]; | ||||
for( int y = 0; y<height; y++){ | ||||
size_t read = fread( line_buf, sizeof( unsigned int ), paddedLineWidth, fh ) | ||||
; | ||||
if( y == 0 && read == 0 ) { // End of file reached | ||||
delete [] line_buf; | ||||
return false; | ||||
} | ||||
else if (read != paddedLineWidth){ | ||||
delete [] line_buf; | ||||
throw pfs::Exception("Error when reading YUV file"); | ||||
} | ||||
else{ | ||||
//who came up with this insane format | ||||
//we read 4 words at a time :'( | ||||
//loop over image pixels, therefore actual data lies in 2* our iteration v | ||||
ariable | ||||
//+= 6 because we write to 6 pixels below | ||||
unsigned int bufIndex; | ||||
for (int x = 0; x<width; x+=6){ | ||||
unsigned int word; | ||||
bufIndex = (x/6)*4; | ||||
//we store chroma in even cells, will interpolate later | ||||
//first block | ||||
word = line_buf[bufIndex]; | ||||
parse210Block(word, Crdest, Ydest, Cbdest, x, x, x, y, false); | ||||
//second block | ||||
word = line_buf[bufIndex + 1]; | ||||
parse210Block(word, Ydest, Cbdest, Ydest, x+2, x+2, x+1, y, true); | ||||
//third block | ||||
word = line_buf[bufIndex + 2]; | ||||
parse210Block(word, Cbdest, Ydest, Crdest, x+4, x+3, x+2, y, false); | ||||
//forth block | ||||
word = line_buf[bufIndex + 3]; | ||||
parse210Block(word, Ydest, Crdest, Ydest, x+5, x+4, x+4, y, true); | ||||
} | ||||
} | ||||
} | ||||
delete[] line_buf; | ||||
upscale_422to444(Cbdest); | ||||
upscale_422to444(Crdest); | ||||
return true; | ||||
} | ||||
void removeCharsFromString(std::string &str, char* charsToRemove ) { | void removeCharsFromString(std::string &str, char* charsToRemove ) { | |||
for ( unsigned int i = 0; i < strlen(charsToRemove); ++i ) { | for ( unsigned int i = 0; i < strlen(charsToRemove); ++i ) { | |||
str.erase( remove(str.begin(), str.end(), charsToRemove[i]), str.end() ) ; | str.erase( remove(str.begin(), str.end(), charsToRemove[i]), str.end() ) ; | |||
} | } | |||
} | } | |||
int parseIntField(std::string i){ | int parseIntField(std::string i){ | |||
//parses an integer field. Removes pesky characters like the b in 10b or the p in 1080p | //parses an integer field. Removes pesky characters like the b in 10b or the p in 1080p | |||
removeCharsFromString(i, charsToRemove); | removeCharsFromString(i, charsToRemove); | |||
return std::atoi(i.data()); | return std::atoi(i.data()); | |||
skipping to change at line 107 | skipping to change at line 240 | |||
std::transform(s2.begin(), s2.end(), s2.begin(), ::tolower); | std::transform(s2.begin(), s2.end(), s2.begin(), ::tolower); | |||
return s1.find(s2) != std::string::npos || s1 == s2; | return s1.find(s2) != std::string::npos || s1 == s2; | |||
} | } | |||
//returns a vector of strings tokenized | //returns a vector of strings tokenized | |||
std::vector<std::string> split(std::string s, std::string delim){ | std::vector<std::string> split(std::string s, std::string delim){ | |||
return std::vector<std::string>(); | return std::vector<std::string>(); | |||
} | } | |||
bool parseFileName(const char * fileNameIn, int * width, int * height, int * bi tdepth, pfs::ColorSpace * colorspace, bool * upscale_420, int * fps){ | bool parseFileName(const char * fileNameIn, int * width, int * height, int * bi tdepth, pfs::ColorSpace * colorspace, ChromaFormat * chromaFormat, int * fps){ | |||
try{ | try{ | |||
/* Parses the filename, puts the result into fields pointed to by the argume nts | /* Parses the filename, puts the result into fields pointed to by the argume nts | |||
returns true if all went well, false if there's a malformed filename | returns true if all went well, false if there's a malformed filename | |||
string should be <prefix>_<width>x<height(p)>_<fps>_<bitdepth(b)>_<colorspa ce>_<chroma_format> | string should be <prefix>_<width>x<height(p)>_<fps>_<bitdepth(b)>_<colorspa ce>_<chroma_format> | |||
Regex would be nice*/ | Regex would be nice*/ | |||
std::string fileName(fileNameIn); | std::string fileName(fileNameIn); | |||
const std::string delimiter = "_"; | const std::string delimiter = "_"; | |||
size_t pos = 0; | size_t pos = 0; | |||
std::string token; | std::string token; | |||
std::vector<std::string> tokens; | std::vector<std::string> tokens; | |||
while ((pos = fileName.find(delimiter)) != std::string::npos) { | while ((pos = fileName.find(delimiter)) != std::string::npos) { | |||
token = fileName.substr(0, pos); | token = fileName.substr(0, pos); | |||
tokens.push_back(token); | tokens.push_back(token); | |||
fileName.erase(0, pos + delimiter.length()); | fileName.erase(0, pos + delimiter.length()); | |||
} | } | |||
tokens.push_back(fileName); | tokens.push_back(fileName); | |||
for (std::vector<string>::iterator tok = tokens.begin() + 1; tok != tokens.e nd(); ++tok) { | for (std::vector<std::string>::iterator tok = tokens.begin() + 1; tok != tok ens.end(); ++tok) { | |||
if (contains(*tok, "x")){ | if (contains(*tok, "x")){ | |||
sscanf(tok->c_str(), "%dx%d", width, height); | sscanf(tok->c_str(), "%dx%d", width, height); | |||
} | } | |||
else if(*tok == "10" || *tok == "8" || *tok == "12" || tok->at(tok->length () - 1) == 'b' || contains(*tok, "bit")){ //last character is a b | else if(*tok == "10" || *tok == "8" || *tok == "12" || tok->at(tok->length () - 1) == 'b' || contains(*tok, "bit")){ //last character is a b | |||
*bitdepth = parseIntField(*tok); | *bitdepth = parseIntField(*tok); | |||
} | } | |||
else if(*tok == "24" || *tok == "25" || *tok == "50" || *tok == "60" || co ntains(*tok, "fps")){ | else if(*tok == "24" || *tok == "25" || *tok == "50" || *tok == "60" || co ntains(*tok, "fps")){ | |||
*fps = parseIntField(*tok); | *fps = parseIntField(*tok); | |||
} | } | |||
else if (contains(*tok, "pq") || (contains(*tok, "2020") && !contains(*tok , "hlg"))){ | else if (contains(*tok, "pq") || (contains(*tok, "2020") && !contains(*tok , "hlg"))){ | |||
*colorspace = pfs::CS_PQYCbCr2020; | *colorspace = pfs::CS_PQYCbCr2020; | |||
} | } | |||
else if (contains(*tok, "bt") || contains(*tok, "709")){ | else if (contains(*tok, "bt") || contains(*tok, "709")){ | |||
*colorspace = pfs::CS_YCbCr709; | *colorspace = pfs::CS_YCbCr709; | |||
} | } | |||
else if (contains(*tok, "hlg")){ | else if (contains(*tok, "hlg")){ | |||
*colorspace = pfs::CS_HLGYCbCr2020; | *colorspace = pfs::CS_HLGYCbCr2020; | |||
} | } | |||
else if(contains(*tok, "444") || contains(*tok, "420")){ | else if(contains(*tok, "444")) *chromaFormat = PL444; | |||
*upscale_420 = contains(*tok, "420"); | else if(contains(*tok, "420")) *chromaFormat = PL420; | |||
} | else if(contains(*tok, "V210")) *chromaFormat = V210; | |||
} | } | |||
return true; | return true; | |||
} | } | |||
catch(...){ | catch(...){ | |||
return false; | return false; | |||
} | } | |||
} | } | |||
void upscale_420to444( pfs::Array2D *ch ) | ||||
{ | ||||
const int height = ch->getRows(); | ||||
const int width = ch->getCols(); | ||||
// For even rows | ||||
for( int y = 0; y < height; y += 2 ) { | ||||
float v_l = (*ch)(0,y); | ||||
float v_r; | ||||
int x; | ||||
for( x = 1; x < (width-1); x += 2 ) { | ||||
v_r = (*ch)(x+1,y); | ||||
// Interpolate between pixels on the left and right | ||||
(*ch)(x,y) = (v_l + v_r)/2.f; | ||||
v_l = v_r; | ||||
} | ||||
if( x < width ) | ||||
(*ch)(x,y) = v_r; | ||||
} | ||||
// For odd rows | ||||
int y; | ||||
for( y = 1; y < (height-1); y += 2 ) { | ||||
for( int x = 0; x < width; x++ ) { | ||||
(*ch)(x,y) = ((*ch)(x,y-1) + (*ch)(x,y+1))/2.f; | ||||
} | ||||
} | ||||
// Last row | ||||
if( y < height ) { | ||||
for( int x = 0; x < width; x++ ) { | ||||
(*ch)(x,y) = (*ch)(x,y-1); | ||||
} | ||||
} | ||||
} | ||||
class YUVReader | class YUVReader | |||
{ | { | |||
FILE *fh; | FILE *fh; | |||
int width, height; | int width, height; | |||
int bit_depth; | int bit_depth; | |||
bool subsampling_420; | ChromaFormat chromaFormat; | |||
unsigned long int fileSize; | unsigned long int fileSize; | |||
enum ColorSpace { REC709, REC2020 }; | enum ColorSpace { REC709, REC2020 }; | |||
ColorSpace color_space; | ColorSpace color_space; | |||
public: | public: | |||
YUVReader( FILE *fh, int width, int height, int bit_depth, bool subsampling_42 0 ) : fh(fh), width( width ), height( height ), bit_depth( bit_depth ), subsampl ing_420( subsampling_420 ) | YUVReader( FILE *fh, int width, int height, int bit_depth, ChromaFormat chroma Format) : fh(fh), width( width ), height( height ), bit_depth( bit_depth ), chro maFormat( chromaFormat ) | |||
{ | { | |||
//initialize filesize | //initialize filesize | |||
fseek(fh, 0L, SEEK_END); | fseek(fh, 0L, SEEK_END); | |||
fileSize = ftell(fh); | fileSize = ftell(fh); | |||
//go back from where we came | //go back from where we came | |||
fseek(fh, 0L, SEEK_SET); | fseek(fh, 0L, SEEK_SET); | |||
} | } | |||
int getWidth() const | int getWidth() const | |||
{ | { | |||
skipping to change at line 236 | skipping to change at line 334 | |||
} | } | |||
/* | /* | |||
Advances a number of frames through the file without reading them */ | Advances a number of frames through the file without reading them */ | |||
// bool advanceFrames(int frameNo){ | // bool advanceFrames(int frameNo){ | |||
// int divisor = subsampling_420 ? 4 : 1; //this is the integer divisor of th e green and blue channels (they are 4 times smaller if 4:2:0 subsampling is enab led) | // int divisor = subsampling_420 ? 4 : 1; //this is the integer divisor of th e green and blue channels (they are 4 times smaller if 4:2:0 subsampling is enab led) | |||
// long int offset = frameNo * (/*Red:*/ width * height * sizeof(STORE_FORMAT ) + /*Green, Blue:*/ 2*width*height*sizeof(STORE_FORMAT)/divisor); | // long int offset = frameNo * (/*Red:*/ width * height * sizeof(STORE_FORMAT ) + /*Green, Blue:*/ 2*width*height*sizeof(STORE_FORMAT)/divisor); | |||
// return fseek(fh, offset, SEEK_CUR); | // return fseek(fh, offset, SEEK_CUR); | |||
// } | // } | |||
/* See to a given frame */ | /* Seek to a given frame */ | |||
bool seekToFrame(int frameNo){ | bool seekToFrame(int frameNo){ | |||
unsigned long int offset = getFileOffsetFromFrame(frameNo); | unsigned long int offset = getFileOffsetFromFrame(frameNo); | |||
if(offset > fileSize){ | if(offset > fileSize){ | |||
throw pfs::Exception("Seeking past EOF, is your given frame range within t he range of the input?"); | throw pfs::Exception("Seeking past EOF, is your given frame range within t he range of the input?"); | |||
} | } | |||
else{ | else{ | |||
return fseek(fh, offset, SEEK_SET); | return fseek(fh, offset, SEEK_SET); | |||
} | } | |||
} | } | |||
//gets the position in the file of the specified frame | ||||
unsigned long int getFileOffsetFromFrame(int frameNo){ | unsigned long int getFileOffsetFromFrame(int frameNo){ | |||
unsigned long int divisor = subsampling_420 ? 4 : 1; //this is the integer d | if(chromaFormat == V210){ | |||
ivisor of the green and blue channels (they are 4 times smaller if 4:2:0 subsamp | /*TODO This*/ | |||
ling is enabled) | return (unsigned long int) ceil(((float) width)/6)*16*height*frameNo; | |||
} | ||||
else{ | ||||
unsigned long int divisor = chromaFormat == PL420 ? 4 : 1; //this is the i | ||||
nteger divisor of the green and blue channels (they are 4 times smaller if 4:2:0 | ||||
subsampling is enabled) | ||||
if( frameNo <= 0 ) | if( frameNo <= 0 ) | |||
throw pfs::Exception( "Invalid frame index. Frame are indexed from 1" ); | throw pfs::Exception( "Invalid frame index. Frame are indexed from 1" ); | |||
unsigned long int storeFormatSize = bit_depth <= 8 ? sizeof(unsigned char) : | unsigned long int storeFormatSize = bit_depth <= 8 ? sizeof(unsigned char) | |||
sizeof(unsigned short); | : sizeof(unsigned short); | |||
unsigned long int offset = (frameNo-1) * (/*Luma:*/ width * height * storeFo | unsigned long int offset = (frameNo-1) * (/*Luma:*/ width * height * store | |||
rmatSize + /*CrCb:*/ 2*width*height*storeFormatSize/divisor); | FormatSize + /*CrCb:*/ 2*width*height*storeFormatSize/divisor); | |||
return offset; | return offset; | |||
} | ||||
} | } | |||
/** | /** | |||
* Read a single frame from Yuv video file and store in 3 RGB channels of type pfs::Array. | * Read a single frame from Yuv video file and store in 3 RGB channels of type pfs::Array. | |||
* Integer values are converted into floating point values. | * Integer values are converted into floating point values. | |||
* | * | |||
* @return TRUE if the frame has been loaded successfully, FALSE if end-of-fil e. | * @return TRUE if the frame has been loaded successfully, FALSE if end-of-fil e. | |||
* Exception is thrown if there is an issue reading a full frame. | * Exception is thrown if there is an issue reading a full frame. | |||
*/ | */ | |||
bool readImage( pfs::Array2D *R, pfs::Array2D *G, pfs::Array2D *B ){ | bool readImage( pfs::Array2D *R, pfs::Array2D *G, pfs::Array2D *B ){ | |||
if(bit_depth <= 8){ | if(chromaFormat == V210){ | |||
return readImageTyped<unsigned char>(R, G, B); | return read_v210_yuv(fh, width, height, R, G, B); | |||
} | } | |||
else{ | else{ | |||
return readImageTyped<unsigned short>(R, G, B); | if(bit_depth <= 8){ | |||
return readImageTyped<unsigned char>(R, G, B); | ||||
} | ||||
else{ | ||||
return readImageTyped<unsigned short>(R, G, B); | ||||
} | ||||
} | } | |||
} | } | |||
private: | private: | |||
template<typename T> | template<typename T> | |||
bool readImageTyped( pfs::Array2D *R, pfs::Array2D *G, pfs::Array2D *B) | bool readImageTyped( pfs::Array2D *R, pfs::Array2D *G, pfs::Array2D *B) | |||
{ | { | |||
{ // Read Y | { // Read Y | |||
const float offset = 16.f/219.f; | const float offset = 16.f/219.f; | |||
skipping to change at line 294 | skipping to change at line 404 | |||
return false; | return false; | |||
} | } | |||
} | } | |||
{ // Read Cb, Cr | { // Read Cb, Cr | |||
const float offset = 128.f/224.f; | const float offset = 128.f/224.f; | |||
const float gain = 1.f/( (float)(1<<(bit_depth-8))*224.f); | const float gain = 1.f/( (float)(1<<(bit_depth-8))*224.f); | |||
unsigned int chroma_width = width; | unsigned int chroma_width = width; | |||
unsigned int chroma_height = height; | unsigned int chroma_height = height; | |||
unsigned int chroma_stride = 0; | unsigned int chroma_stride = 0; | |||
if (subsampling_420){ | if (chromaFormat == PL420){ | |||
chroma_width = width/2; | chroma_width = width/2; | |||
chroma_height = height/2; | chroma_height = height/2; | |||
chroma_stride = 1; | chroma_stride = 1; | |||
} | } | |||
if( !read_yuv_channel<T>( fh, chroma_width, chroma_height, chroma_stride , G, gain, offset, -0.5f, 0.5f ) ) | if( !read_yuv_channel<T>( fh, chroma_width, chroma_height, chroma_stride , G, gain, offset, -0.5f, 0.5f ) ) | |||
throw pfs::Exception( "EOF reached when reading the Cb portion of a fram e" ); | throw pfs::Exception( "EOF reached when reading the Cb portion of a fram e" ); | |||
if( !read_yuv_channel<T>( fh, chroma_width, chroma_height, chroma_stride , B, gain, offset, -0.5f, 0.5f ) ) | if( !read_yuv_channel<T>( fh, chroma_width, chroma_height, chroma_stride , B, gain, offset, -0.5f, 0.5f ) ) | |||
throw pfs::Exception( "EOF reached when reading the Cr portion of a fram e" ); | throw pfs::Exception( "EOF reached when reading the Cr portion of a fram e" ); | |||
if(subsampling_420){ | if(chromaFormat == PL420){ | |||
upscale_420to444( B ); | upscale_420to444( B ); | |||
upscale_420to444( G ); | upscale_420to444( G ); | |||
} | } | |||
} | } | |||
return true; | return true; | |||
} | } | |||
}; | }; | |||
class QuietException | class QuietException | |||
{ | { | |||
skipping to change at line 432 | skipping to change at line 542 | |||
FILE * fh; | FILE * fh; | |||
if(filename != NULL){ | if(filename != NULL){ | |||
fh = fopen (filename, "rb"); | fh = fopen (filename, "rb"); | |||
} | } | |||
if( fh == NULL ) { | if( fh == NULL ) { | |||
throw pfs::Exception("Couldn't find a filename to open, did you provide one" ); | throw pfs::Exception("Couldn't find a filename to open, did you provide one" ); | |||
}; // No more frames | }; // No more frames | |||
int width = -1, height = -1, bitdepth = -1, frame_min = 1, frame_max = INT_MAX , frame_stride = 1, fps = -1; | int width = -1, height = -1, bitdepth = -1, frame_min = 1, frame_max = INT_MAX , frame_stride = 1, fps = -1; | |||
bool upscale_420 = true; | ChromaFormat chromaFormat = PL444; | |||
pfs::ColorSpace colorspace = pfs::CS_INVALID; | pfs::ColorSpace colorspace = pfs::CS_INVALID; | |||
VERBOSE_STR << "reading file '" << filename << "'" << std::endl; | VERBOSE_STR << "reading file '" << filename << "'" << std::endl; | |||
//we infer the metadata from the filename unless noguess is specified | //we infer the metadata from the filename unless noguess is specified | |||
if(!opt_noguess){ | if(!opt_noguess){ | |||
std::string rawName(filename); //these three lines extract the filename prop er, without the containing directory prefix | std::string rawName(filename); //these three lines extract the filename prop er, without the containing directory prefix | |||
long unsigned int substring = rawName.find_last_of("\\/"); | long unsigned int substring = rawName.find_last_of("\\/"); | |||
rawName = rawName.substr(substring == std::string::npos ? 0 : substring); | rawName = rawName.substr(substring == std::string::npos ? 0 : substring); | |||
bool goodFileName = parseFileName(rawName.c_str(), &width, &height, &bitdept h, &colorspace, &upscale_420, &fps); | bool goodFileName = parseFileName(rawName.c_str(), &width, &height, &bitdept h, &colorspace, &chromaFormat, &fps); | |||
if(!goodFileName){ | if(!goodFileName){ | |||
VERBOSE_STR << "Unable to parse filename: " << filename << "\n"; | VERBOSE_STR << "Unable to parse filename: " << filename << "\n"; | |||
} | } | |||
} | } | |||
// Over-ride the auto-recognized params with the ones specified as arguments | // Over-ride the auto-recognized params with the ones specified as arguments | |||
if( opt_width > -1 ) | if( opt_width > -1 ) | |||
width = opt_width; | width = opt_width; | |||
if( opt_height > -1 ) | if( opt_height > -1 ) | |||
height = opt_height; | height = opt_height; | |||
if( opt_bitdepth > -1 ) | if( opt_bitdepth > -1 ) | |||
bitdepth = opt_bitdepth; | bitdepth = opt_bitdepth; | |||
if( opt_frame_min > -1 ) | if( opt_frame_min > -1 ) | |||
frame_min = opt_frame_min; | frame_min = opt_frame_min; | |||
if( opt_frame_max > -1 ) | if( opt_frame_max > -1 ) | |||
frame_max = opt_frame_max; | frame_max = opt_frame_max; | |||
if( opt_frame_stride != 0) | if( opt_frame_stride != 0) | |||
frame_stride = opt_frame_stride; | frame_stride = opt_frame_stride; | |||
if( opt_colorspace != pfs::CS_INVALID ) | if( opt_colorspace != pfs::CS_INVALID ) | |||
colorspace = opt_colorspace; | colorspace = opt_colorspace; | |||
if( opt_chroma_subsampling != NULL ){ | if( opt_chroma_subsampling != NULL ){ | |||
// bad reading, essentially just force upscaling if they input the subsampli | //set up our chromat supsampling, defaults to planar 444 | |||
ng as 420 | chromaFormat = | |||
upscale_420 = strcmp(opt_chroma_subsampling, "420") == 0; | strcmp(opt_chroma_subsampling, "420") == 0 ? PL420 : | |||
strcmp(opt_chroma_subsampling, "V210") == 0 ? V210 : | ||||
PL444; | ||||
} | } | |||
if( opt_fps > 0 ){ | if( opt_fps > 0 ){ | |||
fps = opt_fps; | fps = opt_fps; | |||
} | } | |||
VERBOSE_STR << "Yuv file " << width << "x" << height << " " << bitdepth << "bi ts" << std::endl; | VERBOSE_STR << "Yuv file " << width << "x" << height << " " << bitdepth << "bi ts" << std::endl; | |||
switch( colorspace ) { | switch( colorspace ) { | |||
case pfs::CS_PQYCbCr2020: | case pfs::CS_PQYCbCr2020: | |||
VERBOSE_STR << "colorspace: HDR PQ BT2020" << std::endl; | VERBOSE_STR << "colorspace: HDR PQ BT2020" << std::endl; | |||
break; | break; | |||
skipping to change at line 498 | skipping to change at line 612 | |||
if( frame_min < 0 || frame_max < 0){ | if( frame_min < 0 || frame_max < 0){ | |||
throw pfs::Exception( "Frame range is invalid "); | throw pfs::Exception( "Frame range is invalid "); | |||
} | } | |||
if( colorspace == pfs::CS_INVALID ){ | if( colorspace == pfs::CS_INVALID ){ | |||
throw pfs::Exception( "Unspecified colorspace of the Yuv file" ); | throw pfs::Exception( "Unspecified colorspace of the Yuv file" ); | |||
} | } | |||
if( fps > 0 ){ | if( fps > 0 ){ | |||
VERBOSE_STR << "FPS: " << fps << std::endl; | VERBOSE_STR << "FPS: " << fps << std::endl; | |||
} | } | |||
YUVReader reader( fh, width, height, bitdepth, upscale_420); | YUVReader reader( fh, width, height, bitdepth, chromaFormat); | |||
int currentFrame = frame_min; | int currentFrame = frame_min; | |||
while( (currentFrame <= frame_max && frame_stride > 0) | while( (currentFrame <= frame_max && frame_stride > 0) | |||
|| (currentFrame >= frame_max && frame_stride < 0) ) { //inclusive | || (currentFrame >= frame_max && frame_stride < 0) ) { //inclusive | |||
pfs::Frame *frame = pfsio.createFrame( reader.getWidth(), | pfs::Frame *frame = pfsio.createFrame( reader.getWidth(), | |||
reader.getHeight() ); | reader.getHeight() ); | |||
pfs::Channel *X, *Y, *Z; | pfs::Channel *X, *Y, *Z; | |||
frame->createXYZChannels( X, Y, Z ); | frame->createXYZChannels( X, Y, Z ); | |||
End of changes. 26 change blocks. | ||||
67 lines changed or deleted | 193 lines changed or added |