15 #include "imageTransformColors.h" 16 #include "string_utils.h" 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, NULL);
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, NULL, &_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, NULL, &_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, NULL, &_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, NULL, &_mat);
74 (
"mat3",
"m00,m01,m02,m10,m11,m12,m20,m21,m22", 0,
75 "Defines an arbitrary 3x3 RGB matrix.",
76 &ImageTransformColors::dispatch_mat3, NULL, &_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);
110 void ImageTransformColors::
115 Filenames::iterator fi;
116 for (fi = _filenames.begin(); fi != _filenames.end(); ++fi) {
117 const Filename &source_filename = (*fi);
118 nout << source_filename <<
"\n";
120 if (!image.
read(source_filename)) {
121 nout <<
"Couldn't read " << source_filename <<
"; ignoring.\n";
125 process_image(image);
127 Filename output_filename = get_output_filename(source_filename);
128 if (!image.
write(output_filename)) {
129 nout <<
"Couldn't write " << output_filename <<
"; ignoring.\n";
139 bool ImageTransformColors::
140 dispatch_mat4(
const string &opt,
const string &arg,
void *var) {
144 tokenize(arg, words,
",");
148 if (words.size() == 16) {
150 string_to_double(words[0], mat[0][0]) &&
151 string_to_double(words[1], mat[0][1]) &&
152 string_to_double(words[2], mat[0][2]) &&
153 string_to_double(words[3], mat[0][3]) &&
154 string_to_double(words[4], mat[1][0]) &&
155 string_to_double(words[5], mat[1][1]) &&
156 string_to_double(words[6], mat[1][2]) &&
157 string_to_double(words[7], mat[1][3]) &&
158 string_to_double(words[8], mat[2][0]) &&
159 string_to_double(words[9], mat[2][1]) &&
160 string_to_double(words[10], mat[2][2]) &&
161 string_to_double(words[11], mat[2][3]) &&
162 string_to_double(words[12], mat[3][0]) &&
163 string_to_double(words[13], mat[3][1]) &&
164 string_to_double(words[14], mat[3][2]) &&
165 string_to_double(words[15], mat[3][3]);
170 <<
" requires sixteen numbers separated by commas.\n";
184 bool ImageTransformColors::
185 dispatch_mat3(
const string &opt,
const string &arg,
void *var) {
189 tokenize(arg, words,
",");
193 if (words.size() == 9) {
195 string_to_double(words[0], mat[0][0]) &&
196 string_to_double(words[1], mat[0][1]) &&
197 string_to_double(words[2], mat[0][2]) &&
198 string_to_double(words[3], mat[1][0]) &&
199 string_to_double(words[4], mat[1][1]) &&
200 string_to_double(words[5], mat[1][2]) &&
201 string_to_double(words[6], mat[2][0]) &&
202 string_to_double(words[7], mat[2][1]) &&
203 string_to_double(words[8], mat[2][2]);
208 <<
" requires nine numbers separated by commas.\n";
222 bool ImageTransformColors::
223 dispatch_range(
const string &opt,
const string &arg,
void *var) {
227 tokenize(arg, words,
",");
231 if (words.size() == 2) {
233 string_to_double(words[0], min) &&
234 string_to_double(words[1], max);
239 <<
" requires two numbers separated by commas.\n";
253 bool ImageTransformColors::
254 dispatch_scale(
const string &opt,
const string &arg,
void *var) {
258 tokenize(arg, words,
",");
262 if (words.size() == 3) {
264 string_to_double(words[0], r) &&
265 string_to_double(words[1], g) &&
266 string_to_double(words[2], b);
271 <<
" requires three numbers separated by commas.\n";
285 bool ImageTransformColors::
286 dispatch_add(
const string &opt,
const string &arg,
void *var) {
290 tokenize(arg, words,
",");
294 if (words.size() == 3) {
296 string_to_double(words[0], r) &&
297 string_to_double(words[1], g) &&
298 string_to_double(words[2], b);
303 <<
" requires three numbers separated by commas.\n";
320 bool ImageTransformColors::
323 nout <<
"You must specify the image file(s) to read on the command line.\n";
328 if (_got_output_filename && args.size() == 1) {
329 if (_got_output_dirname) {
330 nout <<
"Cannot specify both -o and -d.\n";
332 }
else if (_inplace) {
333 nout <<
"Cannot specify both -o and -inplace.\n";
338 if (_got_output_filename) {
339 nout <<
"Cannot use -o when multiple image files are specified.\n";
343 if (_got_output_dirname && _inplace) {
344 nout <<
"Cannot specify both -inplace and -d.\n";
347 }
else if (!_got_output_dirname && !_inplace) {
348 nout <<
"You must specify either -inplace or -d.\n";
354 Args::const_iterator ai;
355 for (ai = args.begin(); ai != args.end(); ++ai) {
358 nout <<
"Image file not found: " << filename <<
"\n";
361 _filenames.push_back(filename);
375 get_output_filename(
const Filename &source_filename)
const {
376 if (_got_output_filename) {
377 nassertr(!_inplace && !_got_output_dirname && _filenames.size() == 1,
Filename());
378 return _output_filename;
380 }
else if (_got_output_dirname) {
388 return source_filename;
392 hue2rgb(
double m1,
double m2,
double h) {
395 return m1 + (m2 - m1) * h * 6.0;
401 return m1 + (m2 - m1) * (2.0/3.0 - h) * 6;
409 double l = max(min(hls[1], 1.0), 0.0);
410 double s = max(min(hls[2], 1.0), 0.0);
418 double m1 = l * 2 - m2;
422 hue2rgb(m1, m2, h - 1.0/3.0));
433 double minval = min(min(r, g), b);
434 double maxval = max(max(r, g), b);
436 double rnorm = 0.0, gnorm = 0.0, bnorm = 0.0;
437 double mdiff = maxval - minval;
438 double msum = maxval + minval;
440 if (maxval == minval) {
445 rnorm = (maxval - r) / mdiff;
446 gnorm = (maxval - g) / mdiff;
447 bnorm = (maxval - b) / mdiff;
452 s = mdiff / (2.0 - msum);
456 h = (6.0 + bnorm - gnorm) / 6.0;
457 }
else if (g == maxval) {
458 h = (2.0 + rnorm - bnorm) / 6.0;
460 h = (4.0 + gnorm - rnorm) / 6.0;
475 void ImageTransformColors::
478 for (
int yi = 0; yi < image.
get_y_size(); ++yi) {
479 for (
int xi = 0; xi < image.
get_x_size(); ++xi) {
482 image.
set_xel(xi, yi, LCAST(
float, rgb));
486 for (
int yi = 0; yi < image.
get_y_size(); ++yi) {
487 for (
int xi = 0; xi < image.
get_x_size(); ++xi) {
490 image.
set_xel(xi, yi, LCAST(
float, rgb));
496 int main(
int argc,
char *argv[]) {
This is a 4-by-4 transform matrix.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
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 read(const Filename &filename, PNMFileType *type=NULL, bool report_unknown_type=true)
Reads the indicated image filename.
static LMatrix4d scale_mat(const LVecBase3d &scale)
Returns a matrix that applies the indicated scale in each of the three axes.
static LMatrix4d translate_mat(const LVecBase3d &trans)
Returns a matrix that applies the indicated translation.
LVecBase3d xform_point(const LVecBase3d &v) const
The matrix transforms a 3-component point (including translation component) and returns the result...
This is a 3-by-3 transform matrix.
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.
static const LMatrix4d & ident_mat()
Returns an identity matrix.
void set_dirname(const string &s)
Replaces the directory part of the filename.
bool write(const Filename &filename, PNMFileType *type=NULL) const
Writes the image to the indicated filename.
This is the base class for all three-component vectors and points.
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.