27ImageTransformColors() {
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();
108void 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";
135bool ImageTransformColors::
136dispatch_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";
178bool ImageTransformColors::
179dispatch_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);
214bool ImageTransformColors::
215dispatch_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);
243bool ImageTransformColors::
244dispatch_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);
273bool ImageTransformColors::
274dispatch_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);
305bool 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);
357get_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;
374hue2rgb(
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;
389hls2rgb(
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));
409rgb2hls(
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);
455void 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));
476int main(
int argc,
char *argv[]) {
The name of a file, such as a texture file or an Egg file.
void set_dirname(const std::string &s)
Replaces the directory part of the filename.
bool exists() const
Returns true if the filename exists on the physical disk, false otherwise.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
LRGBColorf get_xel(int x, int y) const
Returns the RGB color at the indicated pixel.
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
void set_xel(int x, int y, const LRGBColorf &value)
Changes the RGB color at the indicated pixel.
bool write(const Filename &filename, PNMFileType *type=nullptr) const
Writes the image to the indicated filename.
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_...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
double string_to_double(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.