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[]) {
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 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.