26 ImageTransformColors::
27 ImageTransformColors() {
28 set_program_brief(
"transform colors in an image file");
29 set_program_description
30 (
"This program can apply a global color transform to all of the " 31 "pixels in an image, or in a series of images. This can be used, " 32 "for instance, to increase or decrease the dynamic range; or to " 33 "rotate the hue; or to reduce the saturation of colors in the image.\n\n" 35 "Each parameter is encoded in a 4x4 matrix, which modifies the R, G, B " 36 "colors of the image (the alpha values, if any, are not affected). " 37 "RGB values are clamped at 0 and 1 after the operation. " 38 "Multiple parameters are composed together in the order in which they " 43 "Specifies that all of the matrix operations are performed in HLS " 44 "space, instead of the default RGB space. In this mode, the first " 45 "component controls hue, the second controls lightness, and the third " 46 "controls saturation.",
47 &ImageTransformColors::dispatch_none, &_hls,
nullptr);
50 (
"range",
"min,max", 0,
51 "Compresses the overall dynamic range from 0,1 to min,max. If min,max " 52 "exceed 0,1, the dynamic range is expanded. This doesn't make sense in " 54 &ImageTransformColors::dispatch_range,
nullptr, &_mat);
58 "Scales the r,g,b components by the indicated values. In HLS mode, " 59 "the scale is applied to the h,l,s components.",
60 &ImageTransformColors::dispatch_scale,
nullptr, &_mat);
64 "Adds the indicated values to the r,g,b components. In HLS mode, " 65 "the sum is applied to the h,l,s components.",
66 &ImageTransformColors::dispatch_add,
nullptr, &_mat);
69 (
"mat4",
"m00,m01,m02,m03,m10,m11,m12,m13,m20,m21,m22,m23,m30,m31,m32,m33",
70 0,
"Defines an arbitrary 4x4 RGB matrix.",
71 &ImageTransformColors::dispatch_mat4,
nullptr, &_mat);
74 (
"mat3",
"m00,m01,m02,m10,m11,m12,m20,m21,m22", 0,
75 "Defines an arbitrary 3x3 RGB matrix.",
76 &ImageTransformColors::dispatch_mat3,
nullptr, &_mat);
80 "Specify the filename to which the resulting image file will be written. " 81 "This is only valid when there is only one input image file on the command " 82 "line. If you want to process multiple files simultaneously, you must " 83 "use either -d or -inplace.",
84 &ImageTransformColors::dispatch_filename, &_got_output_filename, &_output_filename);
88 "Specify the name of the directory in which to write the resulting image " 89 "files. If you are processing only one image file, this may be omitted " 90 "in lieu of the -o option. If you are processing multiple image files, " 91 "this may be omitted only if you specify -inplace instead.",
92 &ImageTransformColors::dispatch_filename, &_got_output_dirname, &_output_dirname);
96 "If this option is given, the input image files will be rewritten in " 97 "place with the results. This obviates the need to specify -d " 98 "for an output directory; however, it's risky because the original " 99 "input image files are lost.",
100 &ImageTransformColors::dispatch_none, &_inplace);
102 _mat = LMatrix4d::ident_mat();
108 void ImageTransformColors::
113 Filenames::iterator fi;
114 for (fi = _filenames.begin(); fi != _filenames.end(); ++fi) {
115 const Filename &source_filename = (*fi);
116 nout << source_filename <<
"\n";
118 if (!image.
read(source_filename)) {
119 nout <<
"Couldn't read " << source_filename <<
"; ignoring.\n";
123 process_image(image);
125 Filename output_filename = get_output_filename(source_filename);
126 if (!image.
write(output_filename)) {
127 nout <<
"Couldn't write " << output_filename <<
"; ignoring.\n";
135 bool ImageTransformColors::
136 dispatch_mat4(
const string &opt,
const string &arg,
void *var) {
137 LMatrix4d &orig = *(LMatrix4d *)var;
144 if (words.size() == 16) {
166 <<
" requires sixteen numbers separated by commas.\n";
178 bool ImageTransformColors::
179 dispatch_mat3(
const string &opt,
const string &arg,
void *var) {
180 LMatrix4d &orig = *(LMatrix4d *)var;
187 if (words.size() == 9) {
202 <<
" requires nine numbers separated by commas.\n";
206 orig *= LMatrix4d(mat);
214 bool ImageTransformColors::
215 dispatch_range(
const string &opt,
const string &arg,
void *var) {
216 LMatrix4d &orig = *(LMatrix4d *)var;
223 if (words.size() == 2) {
231 <<
" requires two numbers separated by commas.\n";
235 orig *= LMatrix4d::scale_mat(max - min) * LMatrix4d::translate_mat(min);
243 bool ImageTransformColors::
244 dispatch_scale(
const string &opt,
const string &arg,
void *var) {
245 LMatrix4d &orig = *(LMatrix4d *)var;
252 if (words.size() == 3) {
261 <<
" requires three numbers separated by commas.\n";
265 orig *= LMatrix4d::scale_mat(r, g, b);
273 bool ImageTransformColors::
274 dispatch_add(
const string &opt,
const string &arg,
void *var) {
275 LMatrix4d &orig = *(LMatrix4d *)var;
282 if (words.size() == 3) {
291 <<
" requires three numbers separated by commas.\n";
295 orig *= LMatrix4d::translate_mat(r, g, b);
305 bool ImageTransformColors::
308 nout <<
"You must specify the image file(s) to read on the command line.\n";
313 if (_got_output_filename && args.size() == 1) {
314 if (_got_output_dirname) {
315 nout <<
"Cannot specify both -o and -d.\n";
317 }
else if (_inplace) {
318 nout <<
"Cannot specify both -o and -inplace.\n";
323 if (_got_output_filename) {
324 nout <<
"Cannot use -o when multiple image files are specified.\n";
328 if (_got_output_dirname && _inplace) {
329 nout <<
"Cannot specify both -inplace and -d.\n";
332 }
else if (!_got_output_dirname && !_inplace) {
333 nout <<
"You must specify either -inplace or -d.\n";
339 Args::const_iterator ai;
340 for (ai = args.begin(); ai != args.end(); ++ai) {
343 nout <<
"Image file not found: " << filename <<
"\n";
346 _filenames.push_back(filename);
357 get_output_filename(
const Filename &source_filename)
const {
358 if (_got_output_filename) {
359 nassertr(!_inplace && !_got_output_dirname && _filenames.size() == 1,
Filename());
360 return _output_filename;
362 }
else if (_got_output_dirname) {
370 return source_filename;
374 hue2rgb(
double m1,
double m2,
double h) {
377 return m1 + (m2 - m1) * h * 6.0;
383 return m1 + (m2 - m1) * (2.0/3.0 - h) * 6;
389 hls2rgb(
const LRGBColord &hls) {
391 double l = max(min(hls[1], 1.0), 0.0);
392 double s = max(min(hls[2], 1.0), 0.0);
400 double m1 = l * 2 - m2;
402 LRGBColord rgb(hue2rgb(m1, m2, h + 1.0/3.0),
404 hue2rgb(m1, m2, h - 1.0/3.0));
409 rgb2hls(
const LRGBColord &rgb) {
415 double minval = min(min(r, g), b);
416 double maxval = max(max(r, g), b);
418 double rnorm = 0.0, gnorm = 0.0, bnorm = 0.0;
419 double mdiff = maxval - minval;
420 double msum = maxval + minval;
422 if (maxval == minval) {
424 return LRGBColord(0.0, l, 0.0);
427 rnorm = (maxval - r) / mdiff;
428 gnorm = (maxval - g) / mdiff;
429 bnorm = (maxval - b) / mdiff;
434 s = mdiff / (2.0 - msum);
438 h = (6.0 + bnorm - gnorm) / 6.0;
439 }
else if (g == maxval) {
440 h = (2.0 + rnorm - bnorm) / 6.0;
442 h = (4.0 + gnorm - rnorm) / 6.0;
449 return LRGBColord(h, l, s);
455 void ImageTransformColors::
458 for (
int yi = 0; yi < image.
get_y_size(); ++yi) {
459 for (
int xi = 0; xi < image.
get_x_size(); ++xi) {
460 LRGBColord rgb = LCAST(
double, image.
get_xel(xi, yi));
461 rgb = hls2rgb(_mat.xform_point(rgb2hls(rgb)));
462 image.
set_xel(xi, yi, LCAST(
float, rgb));
466 for (
int yi = 0; yi < image.
get_y_size(); ++yi) {
467 for (
int xi = 0; xi < image.
get_x_size(); ++xi) {
468 LRGBColord rgb = LCAST(
double, image.
get_xel(xi, yi));
469 rgb = _mat.xform_point(rgb);
470 image.
set_xel(xi, yi, LCAST(
float, rgb));
476 int main(
int argc,
char *argv[]) {
double string_to_double(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void parse_command_line(int argc, char **argv)
Dispatches on each of the options on the command line, and passes the remaining parameters to handle_...
bool write(const Filename &filename, PNMFileType *type=nullptr) const
Writes the image to the indicated filename.
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
LRGBColorf get_xel(int x, int y) const
Returns the RGB color at the indicated pixel.
The name of a file, such as a texture file or an Egg file.
void tokenize(const string &str, vector_string &words, const string &delimiters, bool discard_repeated_delimiters)
Chops the source string up into pieces delimited by any of the characters specified in delimiters.
void set_dirname(const std::string &s)
Replaces the directory part of the filename.
void set_xel(int x, int y, const LRGBColorf &value)
Changes the RGB color at the indicated pixel.
bool exists() const
Returns true if the filename exists on the disk, false otherwise.