save_image_sh.hh 22.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Copyright (C) 2007 EPITA Research and Development Laboratory (LRDE)
// Copyright (C) 2008 EPITA Research and Development Laboratory (LRDE)
// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
// Olena is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation, version 2 of the License.
//
// Olena is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Olena.  If not, see <http://www.gnu.org/licenses/>.
//
// As a special exception, you may use this file as part of a free
// software project without restriction.  Specifically, if other files
// instantiate templates or use macros or inline functions from this
// file, or you compile this file and link it with other files to produce
// an executable, this file does not by itself cause the resulting
// executable to be covered by the GNU General Public License.  This
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.

#ifndef MLN_IO_PLOT_SAVE_IMAGE_SH_HH
#define MLN_IO_PLOT_SAVE_IMAGE_SH_HH

/// \file
///
/// \brief Define some functions to export to gnuplot format as a script shell.
///
/// Theses routines are dedicated to image visualization. The aim is to display
36
/// image whatever the value used as pixel. The behaviour of gnuplot is more
37
38
39
40
41
/// like xv than imageMagick. A Gnuplot script shell file is a text dump file
/// with a preambule to let gnuplot interpret data. As a script shell, you can
/// launch it (don't forget the permissions), and every thing is packed in it.
/// The script file call gnuplot in batch mode, the result window persists and
/// that's all.
42
///
43
44
45
46
47
48

#include <fstream>
#include <string>

#include <mln/trace/entering.hh>
#include <mln/trace/exiting.hh>
49

50
51
52
53
54
55
56
57
58
59
#include <mln/core/macros.hh>
#include <mln/core/contract.hh>
#include <mln/core/image/image1d.hh>
#include <mln/core/image/image2d.hh>
#include <mln/core/image/image3d.hh>

#include <mln/geom/min_row.hh>
#include <mln/geom/max_row.hh>
#include <mln/geom/min_col.hh>
#include <mln/geom/max_col.hh>
60
61
62

#include <mln/trait/value_.hh>

63
64
65
66
67
68
69
70
71
#include <mln/value/int_u.hh>
#include <mln/value/int_s.hh>
#include <mln/value/rgb.hh>
#include <mln/value/hsl.hh>
#include <mln/value/hsi.hh>


namespace mln
{
72

73
74
  namespace io
  {
75

76
77
78
79
80
    namespace plot
    {

      /// \brief Save an image as a gnuplot script shell.
      ///
81
      /// Every thing is save. The image could be 1d, 2d or 3d. The value of
82
83
84
85
86
87
88
89
      /// the pixels could be int_u<n>, int_s<n>, float, double, hsl_f, hsl_d,
      /// hsi_f, hsi_d and rgb<n>.
      ///
      /// \param[in] img      the image which contains the data to save.
      /// \param[in] filename the name of the unix script shell.
      /// \return             the status of the opening file operation.
      ///
      /// The result depends on the permission to save the file with
90
91
      /// filename parameter as unix path. The script shell file must have the
      /// permission to execute (chmod 755). Launch the script shell to call
92
      /// gnuplot in batchmode with fine parameters.
93

94
95
96
      template <typename I>
      bool save_image_sh(const Image<I>& img, const std::string& filename);

97
98
99
    } // end of namespace mln::io::plot

  } // end of namespace mln::io
100
101
102
103


  namespace io
  {
104

105
106
107
    namespace plot
    {

108
#ifndef MLN_INCLUDE_ONLY
109
110

      //------------------------------------------------------------------------
111
      // Impl.
112
113
      //------------------------------------------------------------------------

114
      namespace impl
115
116
      {

117
118
119
	//----------------------------------------------------------------------
	// save_image_sh_image2d_rgb(const image2d<rgb<n>>&, const string&)
	//----------------------------------------------------------------------
120

121
122
123
124
	template <unsigned n>
	inline
	bool save_image_sh_image2d_rgb(const image2d<value::rgb<n> >& img,
				       const std::string&             filename)
125
	{
126
127
	  trace::entering("mln::io::plot::impl::save_image_sh_image2d_rgb");
	  mln_precondition(img.is_valid());
128

129
130
131
132
133
134
	  std::ofstream           out(filename.c_str());
	  bool                    result  = !out.fail();
	  unsigned                min_row = geom::min_row(img);
	  unsigned                max_row = geom::max_row(img);
	  unsigned                min_col = geom::min_col(img);
	  unsigned                max_col = geom::max_col(img);
135

136
	  if (result)
137
	  {
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
	    mln_piter(image2d< value::rgb<n> >) p(img.domain());

	    // Output data prelude (terminal X11, image).
	    out << "#!/bin/sh"                                     << std::endl;
	    out << "####################################"          << std::endl;
	    out << "# Columns = (x, y, r, g, b)        #"          << std::endl;
	    out << "####################################"          << std::endl;
	    out                                                    << std::endl;
	    out << "gnuplot <<EOF"                                 << std::endl;
	    out << "set terminal x11 persist 1"                    << std::endl;
	    out << "set xrange ["                                  << min_col;
	    out << ":"                                             << max_col;
	    out << "]"                                             << std::endl;
	    out << "set yrange [-"                                 << max_row;
	    out << ":"                                             << min_row;
	    out << "]"                                             << std::endl;
	    out << "plot '-' using 2:(-\\$1):3:4:5 with rgbimage"  << std::endl;

	    // Output data.
	    for_all(p)
	    {
	      out << p.row()        << " ";
	      out << p.col()        << " ";
	      out << img(p).red()   << " ";
	      out << img(p).green() << " ";
	      out << img(p).blue()  << std::endl;
	    }

	    // Close gnuplot data stream.
	    out << "e"                                             << std::endl;
	    out << "EOF"                                           << std::endl;

	    out.close();
171
	  }
172
	  else
173
	  {
174
175
	    std::cerr << "ERROR[mln::io::plot::save_image_sh]:"    << filename;
	    std::cerr << " couldn't be opened !!"                  << std::endl;
176
177
	  }

178
179
	  trace::exiting("mln::io::plot::impl::save_image_sh_image2d_rgb");
	  return result;
180
181
	}

182
183
184
	//----------------------------------------------------------------------
	// save_image_sh_image2d_hsl(const image2d<hsl_<T,T,T>>&, const string&)
	//----------------------------------------------------------------------
185

186
187
188
189
190
191
192
	template <typename T>
	inline
	bool save_image_sh_image2d_hsl(const image2d<value::hsl_<T,T,T> >& img,
				       const std::string&              filename)
	{
	  trace::entering("mln::io::plot::impl::save_image_sh_image2d_hsl");
	  mln_precondition(img.is_valid());
193

194
195
196
197
198
199
	  std::ofstream           out(filename.c_str());
	  bool                    result  = !out.fail();
	  unsigned                min_row = geom::min_row(img);
	  unsigned                max_row = geom::max_row(img);
	  unsigned                min_col = geom::min_col(img);
	  unsigned                max_col = geom::max_col(img);
200

201
	  typedef mln::value::hsl_<T,T,T> hsl;
202

203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
	  if (result)
	  {
	    mln_piter(image2d< hsl >) p(img.domain());

	    // Output data prelude (terminal X11, image).
	    out << "#!/bin/sh"                                     << std::endl;
	    out << "####################################"          << std::endl;
	    out << "# Columns = (x, y, val)            #"          << std::endl;
	    out << "####################################"          << std::endl;
	    out                                                    << std::endl;
	    out << "gnuplot <<EOF"                                 << std::endl;
	    out << "q(l,s)     = (l < 0.5)? (l*(1+s)):(l+s-(l*s))" << std::endl;
	    out << "p(l,s)     = (2.0 * l - q(l,s))"               << std::endl;
	    out << "n(x)       = (x < 0)?(x+1):(x > 1)?(x-1) : (x)"<< std::endl;
	    out                                                    << std::endl;
	    out << "c(p,q,t)   = (t <(1.0/6.0))?(p+(q-p)*6.0*t):\\"<< std::endl;
	    out << "             (t <(1.0/2.0))?(q) :\\"           << std::endl;
	    out << " (t<(2.0/3.0))?(p+(q-p)*6.0*((2.0/3.0)-t)):\\" << std::endl;
	    out << "            (p)"				   << std::endl;
	    out                                                    << std::endl;
	    out << "r(h,s,l) = c(p(l,s),q(l,s),n(h/360.0+1.0/3.0))"<< std::endl;
	    out << "g(h,s,l) = c(p(l,s),q(l,s),n(h/360.0))"        << std::endl;
	    out << "b(h,s,l) = c(p(l,s),q(l,s),n(h/360.0-1.0/3.0))"<< std::endl;
	    out                                                    << std::endl;
	    out << "set terminal x11 persist 1"                    << std::endl;
	    out << "set palette gray"                              << std::endl;
	    out << "set xrange ["                                  << min_col;
	    out << ":"                                             << max_col;
	    out << "]"                                             << std::endl;
	    out << "set yrange [-"                                 << max_row;
	    out << ":"                                             << min_row;
	    out << "]"                                             << std::endl;
	    out << "plot '-' using 2:(-\\$1):\\"                   << std::endl;
	    out << "           (r(\\$3,\\$4,\\$5)):\\"             << std::endl;
	    out << "           (g(\\$3,\\$4,\\$5)):\\"             << std::endl;
	    out << "           (b(\\$3,\\$4,\\$5)) with rgbimage"  << std::endl;

	    // Output data.
	    for_all(p)
	    {
	      out << p.row()      << " ";
	      out << p.col()      << " ";
	      out << img(p).hue() << " ";
	      out << img(p).sat() << " ";
	      out << img(p).lum() << std::endl;
	    }

	    // Close gnuplot data stream.
	    out << "e"                                             << std::endl;
	    out << "EOF"                                           << std::endl;

	    out.close();
	  }
	  else
257
	  {
258
259
	    std::cerr << "ERROR[mln::io::plot::save_image_sh]:"    << filename;
	    std::cerr << " couldn't be opened !!"                  << std::endl;
260
261
	  }

262
263
	  trace::exiting("mln::io::plot::impl::save_image_sh_image2d_hsl");
	  return result;
264
265
	}

266
267
268
	//----------------------------------------------------------------------
	// save_image_sh_image2d_hsi(const image2d<hsi_<T,T,T>>&, const string&)
	//----------------------------------------------------------------------
269

270
271
272
273
274
275
276
	template <typename T>
	inline
	bool save_image_sh_image2d_hsi(const image2d<value::hsi_<T,T,T> >& img,
				       const std::string&              filename)
	{
	  trace::entering("mln::io::plot::impl::save_image_sh_image2d_hsi");
	  mln_precondition(img.is_valid());
277

278
279
280
281
282
283
	  std::ofstream           out(filename.c_str());
	  bool                    result  = !out.fail();
	  unsigned                min_row = geom::min_row(img);
	  unsigned                max_row = geom::max_row(img);
	  unsigned                min_col = geom::min_col(img);
	  unsigned                max_col = geom::max_col(img);
284

285
	  typedef mln::value::hsi_<T,T,T> hsi;
286

287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
	  if (result)
	  {
	    mln_piter(image2d< hsi >) p(img.domain());

	    // Output data prelude (terminal X11, image).
	    out << "#!/bin/sh"                                     << std::endl;
	    out << "####################################"          << std::endl;
	    out << "# Columns = (x, y, val)            #"          << std::endl;
	    out << "####################################"          << std::endl;
	    out                                                    << std::endl;
	    out << "gnuplot <<EOF"                                 << std::endl;
	    out << "teta(h)    = (h/180.0) * pi"                   << std::endl;
	    out << "alpha(s,h) = s*cos(teta(h))"                   << std::endl;
	    out << "beta(s,h)  = s*sin(teta(h))"                   << std::endl;
	    out << "n(x)       = (x < 0)?(x+1) : (x > 1)?(x-1):(x)"<< std::endl;
	    out                                                    << std::endl;
	    out << "c(p,q,t)   = (t <(1.0/6.0))?(p+(q-p)*6.0*t):\\"<< std::endl;
	    out << "             (t <(1.0/2.0))?(q) :\\"           << std::endl;
	    out << " (t <(2.0/3.0))?(p+(q-p)*6.0*((2.0/3.0)-t)):\\"<< std::endl;
	    out << "             (p)"				   << std::endl;
	    out                                                    << std::endl;
	    out << "r(h,s,i)   = (sqrt(3.0)/3.0) * i \\"           << std::endl;
	    out << "           + (2.0/(sqrt(6.0))) * beta(s,h)"    << std::endl;
	    out << "g(h,s,i)   = (sqrt(3.0)/3.0) * i \\"           << std::endl;
	    out << "           + (2.0/(sqrt(2.0))) * alpha(s,h)\\" << std::endl;
	    out << "             - (1.0/(sqrt(6.0))) * beta(s,h)"  << std::endl;
	    out << "b(h,s,i)   = (sqrt(3.0)/3.0) * i \\"           << std::endl;
	    out << "           - (2.0/(sqrt(2.0))) * alpha(s,h) \\"<< std::endl;
	    out << "           - (1.0/(sqrt(6.0))) * beta(s,h)"    << std::endl;
	    out                                                    << std::endl;
	    out << "set terminal x11 persist 1"                    << std::endl;
	    out << "set palette gray"                              << std::endl;
	    out << "set xrange ["                                  << min_col;
	    out << ":"                                             << max_col;
	    out << "]"                                             << std::endl;
	    out << "set yrange [-"                                 << max_row;
	    out << ":"                                             << min_row;
	    out << "]"                                             << std::endl;
	    out << "plot '-' using 2:(-\\$1):\\"                   << std::endl;
	    out << "           (r(\\$3,\\$4,\\$5)):\\"             << std::endl;
	    out << "           (g(\\$3,\\$4,\\$5)):\\"             << std::endl;
	    out << "           (b(\\$3,\\$4,\\$5)) with rgbimage"  << std::endl;

	    // Output data.
	    for_all(p)
	    {
	      out << p.row()       << " ";
	      out << p.col()       << " ";
	      out << img(p).hue()  << " ";
	      out << img(p).sat()  << " ";
	      out << img(p).inty() << std::endl;
	    }

	    // Close gnuplot data stream.
	    out << "e"                                             << std::endl;
	    out << "EOF"                                           << std::endl;

	    out.close();
	  }
	  else
347
	  {
348
349
	    std::cerr << "ERROR[mln::io::plot::save_image_sh]:"    << filename;
	    std::cerr << " couldn't be opened !!"                  << std::endl;
350
351
	  }

352
353
	  trace::exiting("mln::io::plot::impl::save_image_sh_image2d_hsi");
	  return result;
354
355
	}

356
357
358
	//----------------------------------------------------------------------
	// save_image_sh_image2d(const image2d<I>&, const string&)
	//----------------------------------------------------------------------
359

360
361
362
363
364
365
366
	template <typename I>
	inline
	bool save_image_sh_image2d(const image2d<I>&  img,
				   const std::string& filename)
	{
	  trace::entering("mln::io::plot::impl::save_image_sh_image2d");
	  mln_precondition(img.is_valid());
367

368
369
370
371
372
373
	  std::ofstream           out(filename.c_str());
	  bool                    result  = !out.fail();
	  unsigned                min_row = geom::min_row(img);
	  unsigned                max_row = geom::max_row(img);
	  unsigned                min_col = geom::min_col(img);
	  unsigned                max_col = geom::max_col(img);
374

375
	  if (result)
376
	  {
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
	    mln_piter(image2d<I>) p(img.domain());

	    // Output data prelude (terminal X11, image).
	    out << "#!/bin/sh"                                 << std::endl;
	    out << "####################################"      << std::endl;
	    out << "# Columns = (x, y, val)            #"      << std::endl;
	    out << "####################################"      << std::endl;
	    out                                                << std::endl;
	    out << "gnuplot <<EOF"                             << std::endl;
	    out << "set terminal x11 persist 1"                << std::endl;
	    out << "set palette gray"                          << std::endl;
	    out << "set xrange ["                              << min_col;
	    out << ":"                                         << max_col;
	    out << "]"                                         << std::endl;
	    out << "set yrange [-"                             << max_row;
	    out << ":"                                         << min_row;
	    out << "]"                                         << std::endl;
	    out << "plot '-' using 2:(-\\$1):3 with image"     <<std::endl;

	    // Output data.
	    for_all(p)
	    {
	      out << p.row()   << " ";
	      out << p.col()   << " ";
	      out << img(p)    << std::endl;
	    }

	    // Close gnuplot data stream.
	    out << "e"                                         << std::endl;
	    out << "EOF"                                       << std::endl;

	    out.close();
	  }
	  else
	  {
	    std::cerr << "ERROR[mln::io::plot::save_image_sh]:"<< filename;
	    std::cerr << " couldn't be opened !!"              << std::endl;
414
415
	  }

416
417
	  trace::exiting("mln::io::plot::impl::save_image_sh_image2d");
	  return result;
418
419
	}

420
421
422
	//----------------------------------------------------------------------
	// save_image_sh_image1d(const image1d<I>&, const string&)
	//----------------------------------------------------------------------
423

424
425
426
427
428
429
430
	template <typename I>
	inline
	bool save_image_sh_image1d(const image1d<I>&  img,
				   const std::string& filename)
	{
	  trace::entering("mln::io::plot::impl::save_image_sh_image1d");
	  mln_precondition(img.is_valid());
431

432
433
	  std::ofstream           out(filename.c_str());
	  bool                    result = !out.fail();
434

435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
	  if (result)
	  {
	    mln_piter(image1d<I>) p(img.domain());

	    // Output data prelude (terminal X11, impulse).
	    out << "#!/bin/sh"                                  << std::endl;
	    out << "##########################"                 << std::endl;
	    out << "# Two columns = (x, val) #"                 << std::endl;
	    out << "##########################"                 << std::endl;
	    out                                                 << std::endl;
	    out << "gnuplot <<EOF"                              << std::endl;
	    out << "set terminal x11 persist 1"                 << std::endl;
	    out << "plot '-' with impulse"                      << std::endl;

	    // Output data.
	    for_all(p)
	    {
	      out << p.ind() << " ";
	      out << img(p)  << std::endl;
	    }

	    // Close gnuplot data stream.
	    out << "e"                                         << std::endl;
	    out << "EOF"                                       << std::endl;

	    out.close();
	  }
	  else
463
	  {
464
465
	    std::cerr << "ERROR[mln::io::plot::save_image_sh]:"<< filename;
	    std::cerr << " couldn't be opened !!"              << std::endl;
466
467
	  }

468
469
	  trace::exiting("mln::io::plot::impl::save_image_sh_image1d");
	  return result;
470
471
	}

472
473
474
	//----------------------------------------------------------------------
	// save_image_sh_image3d(const image3d<I>&, const string&)
	//----------------------------------------------------------------------
475
476


477
478
479
480
481
482
483
	template <typename I>
	inline
	bool save_image_sh_image3d(const image3d<I>&  img,
				   const std::string& filename)
	{
	  trace::entering("mln::io::plot::impl::save_image_sh_image3d");
	  mln_precondition(img.is_valid());
484

485
486
	  std::ofstream           out(filename.c_str());
	  bool                    result = !out.fail();
487

488
	  if (result)
489
	  {
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
	    mln_piter(image3d<I>) p(img.domain());

	    // Output data prelude (terminal X11, pointtype 0).
	    out << "#!/bin/sh"                                 << std::endl;
	    out << "####################################"      << std::endl;
	    out << "# Columns = (x, y, z, val)         #"      << std::endl;
	    out << "####################################"      << std::endl;
	    out                                                << std::endl;
	    out << "gnuplot <<EOF"                             << std::endl;
	    out << "set terminal x11 persist 1"                << std::endl;
	    out << "splot '-' with points palette pointtype 7" << std::endl;

	    // Output data.
	    for_all(p)
	    {
	      out << p.row()   << " ";
	      out << p.col()   << " ";
	      out << p.sli()   << " ";
	      out << img(p)    << std::endl;
	    }

	    // Close gnuplot data stream.
	    out << "e"                                         << std::endl;
	    out << "EOF"                                       << std::endl;

	    out.close();
	  }
	  else
	  {
	    std::cerr << "ERROR[mln::io::plot::save_image_sh]:"<< filename;
	    std::cerr << " couldn't be opened !!"              << std::endl;
521
522
	  }

523
524
	  trace::exiting("mln::io::plot::impl::save_image_sh_image3d");
	  return result;
525
526
	}

527
528
      } // end of namespace impl

529
530

      //------------------------------------------------------------------------
531
      // Internal.
532
533
      //------------------------------------------------------------------------

534
      namespace internal
535
      {
536
537
538
539
	template <unsigned n>
	inline
	bool save_image_sh_dispatch(const image2d<value::rgb<n> >& img,
				    const std::string&             filename)
540
	{
541
	  return impl::save_image_sh_image2d_rgb(img, filename);
542
	}
543
544
545
546
547

	template <typename T>
	inline
	bool save_image_sh_dispatch(const image2d<value::hsl_<T,T,T> >& img,
				    const std::string&                 filename)
548
	{
549
	  return impl::save_image_sh_image2d_hsl(img, filename);
550
551
	}

552
553
554
555
556
557
558
	template <typename T>
	inline
	bool save_image_sh_dispatch(const image2d<value::hsi_<T,T,T> >& img,
				    const std::string&                 filename)
	{
	  return impl::save_image_sh_image2d_hsi(img, filename);
	}
559
560


561
562
563
564
	template <typename I>
	inline
	bool save_image_sh_dispatch(const image2d<I>&  img,
				    const std::string& filename)
565
	{
566
	  return impl::save_image_sh_image2d(img, filename);
567
	}
568
569
570
571
572

	template <typename I>
	inline
	bool save_image_sh_dispatch(const image1d<I>&  img,
				    const std::string& filename)
573
	{
574
	  return impl::save_image_sh_image1d(img, filename);
575
576
	}

577
578
579
580
	template <typename I>
	inline
	bool save_image_sh_dispatch(const image3d<I>&  img,
				    const std::string& filename)
581
	{
582
	  return impl::save_image_sh_image3d(img, filename);
583
	}
584
585
586
587
588

	template <typename I>
	inline
	bool save_image_sh_dispatch(const Image<I>&    img,
				    const std::string& filename)
589
	{
590
	  return save_image_sh_dispatch(exact(img), filename);
591
	}
592
593

      } // end of namespace mln::io::plot::internal
594
595
596
597
598
599
600
601
602
603
604

      //------------------------------------------------------------------------
      // Facade.
      //------------------------------------------------------------------------

      template <typename I>
      inline
      bool save_image_sh(const Image<I>& img, const std::string& filename)
      {
	trace::entering("mln::io::plot::save_image_sh");

605
	bool result = internal::save_image_sh_dispatch(img, filename);
606
607
608
609
610
611
612
613
614

	trace::exiting("mln::io::plot::save_image_sh");
	return result;
      }


#endif // ! MLN_INCLUDE_ONLY


615
    } // end of namespace mln::io::plot
616

617
  } // end of namespace mln::io
618
619
620
621

} // end of namespace mln

#endif // ! MLN_IO_PLOT_SAVE_IMAGE_SH_HH