comparison libinterp/dldfcn/__magick_read__.cc @ 17118:c97a26408ee0

Implement PixelRegion option for imread(). * imread.m: document new option. * private/__imread__.m: parse new option and set defaults. * __magick_read__.cc (calculate region): new option to calculate the region to be read, shifts in memory required, and output image size. (read_indexed_images, read_images): implement reading of only specific regions of an image. (__magick_read__): get octave_scalar_map at start for simplicity.
author Carnë Draug <carandraug@octave.org>
date Wed, 31 Jul 2013 21:28:48 +0100
parents 59acfe9209dd
children 7066eec3431c
comparison
equal deleted inserted replaced
17117:47b504503a3f 17118:c97a26408ee0
42 #ifdef HAVE_MAGICK 42 #ifdef HAVE_MAGICK
43 43
44 #include <Magick++.h> 44 #include <Magick++.h>
45 #include <clocale> 45 #include <clocale>
46 46
47 static std::map<std::string, octave_idx_type>
48 calculate_region (const octave_scalar_map& options)
49 {
50 std::map<std::string, octave_idx_type> region;
51 const Cell pixel_region = options.getfield ("region").cell_value ();
52
53 // Subtract 1 to account for 0 indexing.
54 const Range rows = pixel_region (0).range_value ();
55 const Range cols = pixel_region (1).range_value ();
56 region["row_start"] = rows.base () -1;
57 region["col_start"] = cols.base () -1;
58 region["row_end"] = rows.limit () -1;
59 region["col_end"] = cols.limit () -1;
60
61 // Length of the area to load into the Image Pixel Cache
62 region["row_cache"] = region["row_end"] - region["row_start"] +1;
63 region["col_cache"] = region["col_end"] - region["col_start"] +1;
64
65 // How much we have to shift in the memory when doing the loops.
66 region["row_shift"] = region["col_cache"] * rows.inc ();
67 region["col_shift"] = region["col_cache"] * region["row_cache"] - cols.inc ();
68
69 // The actual height and width of the output image
70 region["row_out"] = floor ((region["row_end"] - region["row_start"]) / rows.inc ()) + 1;
71 region["col_out"] = floor ((region["col_end"] - region["col_start"]) / cols.inc ()) + 1;
72
73 return region;
74 }
75
47 template <class T> 76 template <class T>
48 static octave_value_list 77 static octave_value_list
49 read_indexed_images (std::vector<Magick::Image>& imvec, 78 read_indexed_images (std::vector<Magick::Image>& imvec,
50 const Array<octave_idx_type>& frameidx, 79 const Array<octave_idx_type>& frameidx,
51 const octave_idx_type nargout) 80 const octave_idx_type nargout,
81 const octave_scalar_map& options)
52 { 82 {
53 typedef typename T::element_type P; 83 typedef typename T::element_type P;
54 84
55 octave_value_list retval (3, Matrix ()); 85 octave_value_list retval (3, Matrix ());
56 86
57 const octave_idx_type nRows = imvec[0].baseRows (); 87 std::map<std::string, octave_idx_type> region = calculate_region (options);
58 const octave_idx_type nCols = imvec[0].baseColumns (); 88 const octave_idx_type nFrames = frameidx.length ();
59 const octave_idx_type nFrames = frameidx.length (); 89 const octave_idx_type nRows = region["row_out"];
90 const octave_idx_type nCols = region["col_out"];
60 91
61 T img = T (dim_vector (nRows, nCols, 1, nFrames)); 92 T img = T (dim_vector (nRows, nCols, 1, nFrames));
62 P* img_fvec = img.fortran_vec (); 93 P* img_fvec = img.fortran_vec ();
94
95 const octave_idx_type row_start = region["row_start"];
96 const octave_idx_type col_start = region["col_start"];
97 const octave_idx_type row_shift = region["row_shift"];
98 const octave_idx_type col_shift = region["col_shift"];
99 const octave_idx_type row_cache = region["row_cache"];
100 const octave_idx_type col_cache = region["col_cache"];
63 101
64 // When reading PixelPackets from the Image Pixel Cache, they come in 102 // When reading PixelPackets from the Image Pixel Cache, they come in
65 // row major order. So we keep moving back and forth there so we can 103 // row major order. So we keep moving back and forth there so we can
66 // write the image in column major order. 104 // write the image in column major order.
67 octave_idx_type idx = 0; 105 octave_idx_type idx = 0;
68 for (octave_idx_type frame = 0; frame < nFrames; frame++) 106 for (octave_idx_type frame = 0; frame < nFrames; frame++)
69 { 107 {
70 imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows); 108 imvec[frameidx(frame)].getConstPixels (col_start, row_start,
109 col_cache, row_cache);
71 110
72 const Magick::IndexPacket *pix 111 const Magick::IndexPacket *pix
73 = imvec[frameidx(frame)].getConstIndexes (); 112 = imvec[frameidx(frame)].getConstIndexes ();
74 113
75 for (octave_idx_type col = 0; col < nCols; col++) 114 for (octave_idx_type col = 0; col < nCols; col++)
76 { 115 {
77 for (octave_idx_type row = 0; row < nRows; row++) 116 for (octave_idx_type row = 0; row < nRows; row++)
78 { 117 {
79 img_fvec[idx++] = static_cast<P> (*pix); 118 img_fvec[idx++] = static_cast<P> (*pix);
80 pix += nCols; 119 pix += row_shift;
81 } 120 }
82 pix -= nCols * nRows -1; 121 pix -= col_shift;
83 } 122 }
84 } 123 }
85 retval(0) = octave_value (img); 124 retval(0) = octave_value (img);
86 125
87 // Do we need to get the colormap to interpret the image and alpha channel? 126 // Do we need to get the colormap to interpret the image and alpha channel?
146 // readability too much. 185 // readability too much.
147 template <class T> 186 template <class T>
148 octave_value_list 187 octave_value_list
149 read_images (std::vector<Magick::Image>& imvec, 188 read_images (std::vector<Magick::Image>& imvec,
150 const Array<octave_idx_type>& frameidx, 189 const Array<octave_idx_type>& frameidx,
151 const octave_idx_type nargout) 190 const octave_idx_type nargout,
191 const octave_scalar_map& options)
152 { 192 {
153 typedef typename T::element_type P; 193 typedef typename T::element_type P;
154 194
155 octave_value_list retval (3, Matrix ()); 195 octave_value_list retval (3, Matrix ());
156 196
157 const octave_idx_type nRows = imvec[0].baseRows (); 197 std::map<std::string, octave_idx_type> region = calculate_region (options);
158 const octave_idx_type nCols = imvec[0].baseColumns ();
159 const octave_idx_type nFrames = frameidx.length (); 198 const octave_idx_type nFrames = frameidx.length ();
199 const octave_idx_type nRows = region["row_out"];
200 const octave_idx_type nCols = region["col_out"];
160 T img; 201 T img;
202
203 const octave_idx_type row_start = region["row_start"];
204 const octave_idx_type col_start = region["col_start"];
205 const octave_idx_type row_shift = region["row_shift"];
206 const octave_idx_type col_shift = region["col_shift"];
207 const octave_idx_type row_cache = region["row_cache"];
208 const octave_idx_type col_cache = region["col_cache"];
161 209
162 // GraphicsMagick (GM) keeps the image values in memory using whatever 210 // GraphicsMagick (GM) keeps the image values in memory using whatever
163 // QuantumDepth it was built with independently of the original image 211 // QuantumDepth it was built with independently of the original image
164 // bitdepth. Basically this means that if GM was built with quantum 16 212 // bitdepth. Basically this means that if GM was built with quantum 16
165 // all values are scaled in the uint16 range. If the original image 213 // all values are scaled in the uint16 range. If the original image
235 283
236 octave_idx_type idx = 0; 284 octave_idx_type idx = 0;
237 for (octave_idx_type frame = 0; frame < nFrames; frame++) 285 for (octave_idx_type frame = 0; frame < nFrames; frame++)
238 { 286 {
239 const Magick::PixelPacket *pix 287 const Magick::PixelPacket *pix
240 = imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows); 288 = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
289 col_cache, row_cache);
241 290
242 for (octave_idx_type col = 0; col < nCols; col++) 291 for (octave_idx_type col = 0; col < nCols; col++)
243 { 292 {
244 for (octave_idx_type row = 0; row < nRows; row++) 293 for (octave_idx_type row = 0; row < nRows; row++)
245 { 294 {
246 img_fvec[idx++] = pix->red / divisor; 295 img_fvec[idx++] = pix->red / divisor;
247 pix += nCols; 296 pix += row_shift;
248 } 297 }
249 pix -= nRows * nCols -1; 298 pix -= col_shift;
250 } 299 }
251 } 300 }
252 break; 301 break;
253 } 302 }
254 303
261 310
262 octave_idx_type idx = 0; 311 octave_idx_type idx = 0;
263 for (octave_idx_type frame = 0; frame < nFrames; frame++) 312 for (octave_idx_type frame = 0; frame < nFrames; frame++)
264 { 313 {
265 const Magick::PixelPacket *pix 314 const Magick::PixelPacket *pix
266 = imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows); 315 = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
316 col_cache, row_cache);
267 317
268 for (octave_idx_type col = 0; col < nCols; col++) 318 for (octave_idx_type col = 0; col < nCols; col++)
269 { 319 {
270 for (octave_idx_type row = 0; row < nRows; row++) 320 for (octave_idx_type row = 0; row < nRows; row++)
271 { 321 {
272 img_fvec[idx] = pix->red / divisor; 322 img_fvec[idx] = pix->red / divisor;
273 a_fvec[idx] = pix->opacity / divisor; 323 a_fvec[idx] = pix->opacity / divisor;
274 pix += nCols; 324 pix += row_shift;
275 idx++; 325 idx++;
276 } 326 }
277 pix -= nRows * nCols -1; 327 pix -= col_shift;
278 } 328 }
279 } 329 }
280 retval(2) = alpha; 330 retval(2) = alpha;
281 break; 331 break;
282 } 332 }
288 P *img_fvec = img.fortran_vec (); 338 P *img_fvec = img.fortran_vec ();
289 339
290 for (octave_idx_type frame = 0; frame < nFrames; frame++) 340 for (octave_idx_type frame = 0; frame < nFrames; frame++)
291 { 341 {
292 const Magick::PixelPacket *pix 342 const Magick::PixelPacket *pix
293 = imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows); 343 = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
344 col_cache, row_cache);
294 345
295 octave_idx_type idx = 0; 346 octave_idx_type idx = 0;
296 img_fvec += nRows * nCols * frame; 347 img_fvec += nRows * nCols * frame;
297 P *rbuf = img_fvec; 348 P *rbuf = img_fvec;
298 P *gbuf = img_fvec + nRows * nCols; 349 P *gbuf = img_fvec + nRows * nCols;
303 for (octave_idx_type row = 0; row < nRows; row++) 354 for (octave_idx_type row = 0; row < nRows; row++)
304 { 355 {
305 rbuf[idx] = pix->red / divisor; 356 rbuf[idx] = pix->red / divisor;
306 gbuf[idx] = pix->green / divisor; 357 gbuf[idx] = pix->green / divisor;
307 bbuf[idx] = pix->blue / divisor; 358 bbuf[idx] = pix->blue / divisor;
308 pix += nCols; 359 pix += row_shift;
309 idx++; 360 idx++;
310 } 361 }
311 pix -= nRows * nCols -1; 362 pix -= col_shift;
312 } 363 }
313 } 364 }
314 break; 365 break;
315 } 366 }
316 367
326 // to be reset on each frame since it's a separate matrix. 377 // to be reset on each frame since it's a separate matrix.
327 octave_idx_type a_idx = 0; 378 octave_idx_type a_idx = 0;
328 for (octave_idx_type frame = 0; frame < nFrames; frame++) 379 for (octave_idx_type frame = 0; frame < nFrames; frame++)
329 { 380 {
330 const Magick::PixelPacket *pix 381 const Magick::PixelPacket *pix
331 = imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows); 382 = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
383 col_cache, row_cache);
332 384
333 octave_idx_type idx = 0; 385 octave_idx_type idx = 0;
334 img_fvec += nRows * nCols * frame; 386 img_fvec += nRows * nCols * frame;
335 P *rbuf = img_fvec; 387 P *rbuf = img_fvec;
336 P *gbuf = img_fvec + nRows * nCols; 388 P *gbuf = img_fvec + nRows * nCols;
342 { 394 {
343 rbuf[idx] = pix->red / divisor; 395 rbuf[idx] = pix->red / divisor;
344 gbuf[idx] = pix->green / divisor; 396 gbuf[idx] = pix->green / divisor;
345 bbuf[idx] = pix->blue / divisor; 397 bbuf[idx] = pix->blue / divisor;
346 a_fvec[a_idx] = pix->opacity / divisor; 398 a_fvec[a_idx] = pix->opacity / divisor;
347 pix += nCols; 399 pix += row_shift;
348 idx++; 400 idx++;
349 } 401 }
350 pix -= nRows * nCols -1; 402 pix -= col_shift;
351 } 403 }
352 } 404 }
353 retval(2) = alpha; 405 retval(2) = alpha;
354 break; 406 break;
355 } 407 }
360 P *img_fvec = img.fortran_vec (); 412 P *img_fvec = img.fortran_vec ();
361 413
362 for (octave_idx_type frame = 0; frame < nFrames; frame++) 414 for (octave_idx_type frame = 0; frame < nFrames; frame++)
363 { 415 {
364 const Magick::PixelPacket *pix 416 const Magick::PixelPacket *pix
365 = imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows); 417 = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
418 col_cache, row_cache);
366 419
367 octave_idx_type idx = 0; 420 octave_idx_type idx = 0;
368 img_fvec += nRows * nCols * frame; 421 img_fvec += nRows * nCols * frame;
369 P *cbuf = img_fvec; 422 P *cbuf = img_fvec;
370 P *mbuf = img_fvec + nRows * nCols; 423 P *mbuf = img_fvec + nRows * nCols;
377 { 430 {
378 cbuf[idx] = pix->red / divisor; 431 cbuf[idx] = pix->red / divisor;
379 mbuf[idx] = pix->green / divisor; 432 mbuf[idx] = pix->green / divisor;
380 ybuf[idx] = pix->blue / divisor; 433 ybuf[idx] = pix->blue / divisor;
381 kbuf[idx] = pix->opacity / divisor; 434 kbuf[idx] = pix->opacity / divisor;
382 pix += nCols; 435 pix += row_shift;
383 idx++; 436 idx++;
384 } 437 }
385 pix -= nRows * nCols -1; 438 pix -= col_shift;
386 } 439 }
387 } 440 }
388 break; 441 break;
389 } 442 }
390 443
400 // to be reset on each frame since it's a separate matrix. 453 // to be reset on each frame since it's a separate matrix.
401 octave_idx_type a_idx = 0; 454 octave_idx_type a_idx = 0;
402 for (octave_idx_type frame = 0; frame < nFrames; frame++) 455 for (octave_idx_type frame = 0; frame < nFrames; frame++)
403 { 456 {
404 const Magick::PixelPacket *pix 457 const Magick::PixelPacket *pix
405 = imvec[frameidx(frame)].getConstPixels (0, 0, nCols, nRows); 458 = imvec[frameidx(frame)].getConstPixels (col_start, row_start,
459 col_cache, row_cache);
406 // Note that for CMYKColorspace + matte (CMYKA), the opacity is 460 // Note that for CMYKColorspace + matte (CMYKA), the opacity is
407 // stored in the assocated IndexPacket. 461 // stored in the assocated IndexPacket.
408 const Magick::IndexPacket *apix 462 const Magick::IndexPacket *apix
409 = imvec[frameidx(frame)].getConstIndexes (); 463 = imvec[frameidx(frame)].getConstIndexes ();
410 464
422 cbuf[idx] = pix->red / divisor; 476 cbuf[idx] = pix->red / divisor;
423 mbuf[idx] = pix->green / divisor; 477 mbuf[idx] = pix->green / divisor;
424 ybuf[idx] = pix->blue / divisor; 478 ybuf[idx] = pix->blue / divisor;
425 kbuf[idx] = pix->opacity / divisor; 479 kbuf[idx] = pix->opacity / divisor;
426 a_fvec[a_idx] = *apix / divisor; 480 a_fvec[a_idx] = *apix / divisor;
427 pix += nCols; 481 pix += row_shift;
428 idx++; 482 idx++;
429 a_idx++; 483 a_idx++;
430 } 484 }
431 pix -= nRows * nCols -1; 485 pix -= col_shift;
432 } 486 }
433 } 487 }
434 retval(2) = alpha; 488 retval(2) = alpha;
435 break; 489 break;
436 } 490 }
522 { 576 {
523 print_usage (); 577 print_usage ();
524 return output; 578 return output;
525 } 579 }
526 580
527 const octave_map options = args(1).map_value (); 581 const octave_scalar_map options = args(1).scalar_map_value ();
528 if (error_state) 582 if (error_state)
529 { 583 {
530 error ("__magick_read__: OPTIONS must be a struct"); 584 error ("__magick_read__: OPTIONS must be a struct");
531 } 585 }
532 586
538 } 592 }
539 593
540 // Prepare an Array with the indexes for the requested frames. 594 // Prepare an Array with the indexes for the requested frames.
541 const octave_idx_type nFrames = imvec.size (); 595 const octave_idx_type nFrames = imvec.size ();
542 Array<octave_idx_type> frameidx; 596 Array<octave_idx_type> frameidx;
543 const octave_value indexes = options.getfield ("index")(0); 597 const octave_value indexes = options.getfield ("index");
544 if (indexes.is_string () && indexes.string_value () == "all") 598 if (indexes.is_string () && indexes.string_value () == "all")
545 { 599 {
546 frameidx.resize (dim_vector (1, nFrames)); 600 frameidx.resize (dim_vector (1, nFrames));
547 for (octave_idx_type i = 0; i < nFrames; i++) 601 for (octave_idx_type i = 0; i < nFrames; i++)
548 { 602 {
592 646
593 if (klass == Magick::PseudoClass && imvec[0].magick () != "JPEG") 647 if (klass == Magick::PseudoClass && imvec[0].magick () != "JPEG")
594 { 648 {
595 if (depth <= 1) 649 if (depth <= 1)
596 { 650 {
597 output = read_indexed_images <boolNDArray> (imvec, frameidx, nargout); 651 output = read_indexed_images <boolNDArray> (imvec, frameidx, nargout, options);
598 } 652 }
599 else if (depth <= 8) 653 else if (depth <= 8)
600 { 654 {
601 output = read_indexed_images <uint8NDArray> (imvec, frameidx, nargout); 655 output = read_indexed_images <uint8NDArray> (imvec, frameidx, nargout, options);
602 } 656 }
603 else if (depth <= 16) 657 else if (depth <= 16)
604 { 658 {
605 output = read_indexed_images <uint16NDArray> (imvec, frameidx, nargout); 659 output = read_indexed_images <uint16NDArray> (imvec, frameidx, nargout, options);
606 } 660 }
607 else 661 else
608 { 662 {
609 error ("imread: indexed images with depths greater than 16-bit are not supported"); 663 error ("imread: indexed images with depths greater than 16-bit are not supported");
610 return output; 664 return output;
613 667
614 else 668 else
615 { 669 {
616 if (depth <= 1) 670 if (depth <= 1)
617 { 671 {
618 output = read_images<boolNDArray> (imvec, frameidx, nargout); 672 output = read_images<boolNDArray> (imvec, frameidx, nargout, options);
619 } 673 }
620 else if (depth <= 8) 674 else if (depth <= 8)
621 { 675 {
622 output = read_images<uint8NDArray> (imvec, frameidx, nargout); 676 output = read_images<uint8NDArray> (imvec, frameidx, nargout, options);
623 } 677 }
624 else if (depth <= 16) 678 else if (depth <= 16)
625 { 679 {
626 output = read_images<uint16NDArray> (imvec, frameidx, nargout); 680 output = read_images<uint16NDArray> (imvec, frameidx, nargout, options);
627 } 681 }
628 else if (depth <= 32) 682 else if (depth <= 32)
629 { 683 {
630 output = read_images<FloatNDArray> (imvec, frameidx, nargout); 684 output = read_images<FloatNDArray> (imvec, frameidx, nargout, options);
631 } 685 }
632 else 686 else
633 { 687 {
634 error ("imread: reading of images with %i-bit depth is not supported", 688 error ("imread: reading of images with %i-bit depth is not supported",
635 depth); 689 depth);