Mercurial > octave-antonio
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); |