-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathjpeg2000.go
More file actions
623 lines (533 loc) · 19.9 KB
/
Copy pathjpeg2000.go
File metadata and controls
623 lines (533 loc) · 19.9 KB
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
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
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
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
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
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
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
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
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
// Package jpeg2000 provides a pure Go implementation of the JPEG 2000 image codec.
//
// JPEG 2000 (ISO/IEC 15444-1) is a wavelet-based image compression standard that
// provides both lossless and lossy compression. This package aims for 100% parity
// with the OpenJPEG reference implementation.
//
// Basic usage for decoding:
//
// file, _ := os.Open("image.jp2")
// img, err := jpeg2000.Decode(file)
// if err != nil {
// log.Fatal(err)
// }
//
// Basic usage for encoding:
//
// file, _ := os.Create("output.jp2")
// err := jpeg2000.Encode(file, img, nil)
// if err != nil {
// log.Fatal(err)
// }
package jpeg2000
import (
"image"
"io"
)
// Format constants for JPEG 2000 file formats.
const (
// FormatJ2K is the raw codestream format (no file wrapper).
FormatJ2K Format = iota
// FormatJP2 is the standard JP2 file format with metadata boxes.
FormatJP2
// FormatJPX is the extended JP2 format (Part 2).
FormatJPX
)
// Format represents a JPEG 2000 file format.
type Format int
// String returns the string representation of the format.
func (f Format) String() string {
switch f {
case FormatJ2K:
return "J2K"
case FormatJP2:
return "JP2"
case FormatJPX:
return "JPX"
default:
return "Unknown"
}
}
// Profile constants for JPEG 2000 profiles (RSIZ parameter).
const (
// ProfileNone indicates no profile restrictions.
ProfileNone Profile = 0x0000
// ProfilePart2 indicates Part 2 extensions are used.
ProfilePart2 Profile = 0x8000
// ProfileCinema2K is the 2K Digital Cinema profile.
ProfileCinema2K Profile = 0x0003
// ProfileCinema4K is the 4K Digital Cinema profile.
ProfileCinema4K Profile = 0x0004
// ProfileCinemaS2K is the 2K scalable Digital Cinema profile.
ProfileCinemaS2K Profile = 0x0005
// ProfileCinemaS4K is the 4K scalable Digital Cinema profile.
ProfileCinemaS4K Profile = 0x0006
// ProfileCinemaSLTE is the Long-term extension Digital Cinema profile.
ProfileCinemaSLTE Profile = 0x0007
// ProfileBroadcastSingle is single-tile broadcast profile.
ProfileBroadcastSingle Profile = 0x0100
// ProfileBroadcastMulti is multi-tile broadcast profile.
ProfileBroadcastMulti Profile = 0x0200
// ProfileIMF2K is 2K Interoperable Master Format profile.
ProfileIMF2K Profile = 0x0400
// ProfileIMF4K is 4K Interoperable Master Format profile.
ProfileIMF4K Profile = 0x0500
// ProfileIMF8K is 8K Interoperable Master Format profile.
ProfileIMF8K Profile = 0x0600
)
// Profile represents a JPEG 2000 profile (RSIZ parameter).
type Profile uint16
// ProgressionOrder defines the order in which packets are encoded/decoded.
type ProgressionOrder int
const (
// LRCP is Layer-Resolution-Component-Position order.
LRCP ProgressionOrder = iota
// RLCP is Resolution-Layer-Component-Position order.
RLCP
// RPCL is Resolution-Position-Component-Layer order.
RPCL
// PCRL is Position-Component-Resolution-Layer order.
PCRL
// CPRL is Component-Position-Resolution-Layer order.
CPRL
)
// String returns the string representation of the progression order.
func (p ProgressionOrder) String() string {
switch p {
case LRCP:
return "LRCP"
case RLCP:
return "RLCP"
case RPCL:
return "RPCL"
case PCRL:
return "PCRL"
case CPRL:
return "CPRL"
default:
return "Unknown"
}
}
// ColorSpace represents the color space of an image.
// Values 0-5 match the OpenJPEG OPJ_COLOR_SPACE enum for compatibility.
// Additional colorspaces from ISO/IEC 15444-1 are assigned values 6+.
type ColorSpace int
const (
// ColorSpaceUnknown indicates the colorspace is not supported.
// This is returned when the JP2 file specifies an unrecognized enumcs value.
ColorSpaceUnknown ColorSpace = iota - 1 // -1 matches OPJ_CLRSPC_UNKNOWN
// ColorSpaceUnspecified indicates no colorspace was specified in the file.
// This is returned for raw J2K codestreams without a JP2 container.
ColorSpaceUnspecified // 0 matches OPJ_CLRSPC_UNSPECIFIED
// ColorSpaceSRGB is standard RGB (enumcs 16).
ColorSpaceSRGB // 1 matches OPJ_CLRSPC_SRGB
// ColorSpaceGray is grayscale (enumcs 17).
ColorSpaceGray // 2 matches OPJ_CLRSPC_GRAY
// ColorSpaceSYCC is sRGB-based YCbCr (enumcs 1, 18).
// Uses ITU-R BT.709-5 matrix with sRGB primaries.
ColorSpaceSYCC // 3 matches OPJ_CLRSPC_SYCC
// ColorSpaceEYCC is extended sYCC (enumcs 24).
// Extended gamut YCbCr based on sRGB.
ColorSpaceEYCC // 4 matches OPJ_CLRSPC_EYCC
// ColorSpaceCMYK is CMYK color space (enumcs 12).
ColorSpaceCMYK // 5 matches OPJ_CLRSPC_CMYK
// ColorSpaceBilevel is bi-level/binary (enumcs 0, 15).
// Note: OpenJPEG maps bilevel to unknown.
ColorSpaceBilevel // 6 (extension beyond OpenJPEG)
// ColorSpaceYCbCr2 is YCbCr for 625-line systems (enumcs 3).
// Uses ITU-R BT.601-5 matrix for PAL/SECAM.
ColorSpaceYCbCr2 // 7
// ColorSpaceYCbCr3 is YCbCr for 525-line systems (enumcs 4).
// Uses ITU-R BT.601-5 matrix for NTSC.
ColorSpaceYCbCr3 // 8
// ColorSpacePhotoYCC is Kodak PhotoYCC (enumcs 9).
// Used in Kodak Photo CD format.
ColorSpacePhotoYCC // 9
// ColorSpaceCMY is CMY without black (enumcs 11).
ColorSpaceCMY // 10
// ColorSpaceYCCK is YCCK (enumcs 13).
// PhotoYCC-based CMYK representation.
ColorSpaceYCCK // 11
// ColorSpaceCIELab is CIE L*a*b* (enumcs 14).
// Device-independent color space with D50 illuminant.
ColorSpaceCIELab // 12
// ColorSpaceCIEJab is CIE J*a*b* (enumcs 19).
// CIECAM02-based appearance model.
ColorSpaceCIEJab // 13
// ColorSpaceESRGB is extended sRGB (enumcs 20).
// Extended gamut sRGB per IEC 61966-2-1 Amendment 1.
ColorSpaceESRGB // 14
// ColorSpaceROMMRGB is ROMM-RGB/ProPhoto RGB (enumcs 21).
// Wide gamut RGB per ISO 22028-2.
ColorSpaceROMMRGB // 15
// ColorSpaceYPbPr60 is YPbPr for 1125/60 systems (enumcs 22).
// HD video per SMPTE 274M.
ColorSpaceYPbPr60 // 16
// ColorSpaceYPbPr50 is YPbPr for 1250/50 systems (enumcs 23).
// HD video per ITU-R BT.1361.
ColorSpaceYPbPr50 // 17
)
// Config holds the decoding configuration.
type Config struct {
// DecodeArea limits the decode to a region, in full-resolution image
// coordinates, and is clipped to the image. Nil decodes all of it.
//
// The returned image covers the region and is allocated for it, so a band
// of a large image costs a band-sized buffer. Precincts that fall outside
// the region are not entropy-decoded, and with an explicit precinct
// partition their packets are not read either — see
// PacketIndex.PacketsForRegion for the byte ranges that implies.
DecodeArea *image.Rectangle
// ReduceResolution specifies the number of resolution levels to skip.
// 0 means full resolution, 1 means half resolution, etc.
ReduceResolution int
// QualityLayers specifies the number of quality layers to decode.
// 0 means all layers.
QualityLayers int
}
// PrecinctSize is one resolution's precinct partition, as base-2 exponents.
// {WidthExp: 7, HeightExp: 7} is a 128x128 precinct.
type PrecinctSize struct {
WidthExp uint8
HeightExp uint8
}
// Options holds the encoding options.
type Options struct {
// Format specifies the output format (J2K, JP2, or JPX).
Format Format
// Profile specifies the JPEG 2000 profile to use.
Profile Profile
// Lossless specifies whether to use lossless compression.
// If true, the 5-3 reversible wavelet transform is used.
// If false, the 9-7 irreversible wavelet transform is used.
Lossless bool
// Quality specifies the compression quality (1-100).
// Only used when Lossless is false. Zero means 100.
//
// It is an error budget rather than a scale factor: it fixes the largest
// difference, in sample counts, between a decoded sample and the source.
// 100 asks for half a count at 8-bit precision and each ten points below
// doubles that, with the budget scaling with the sample range so a 16-bit
// image is held to the same relative accuracy. The quantization step sizes
// the QCD marker carries are derived from that budget and the synthesis
// gain of each subband, so the guarantee holds for any conforming decoder,
// not only for this one. See "Lossy 9/7" in the README.
//
// Half a count cannot survive rounding to an integer sample, so 100
// reproduces 8-bit input exactly. Two limits can widen the steps past what
// was asked at deep decompositions: the five-bit QCD exponent field, and
// the 30 bit-plane ceiling the block coder imposes.
Quality int
// CompressionRatio specifies the target compression ratio.
// Only used when Lossless is false and Quality is 0.
// For example, 20 means 20:1 compression.
CompressionRatio float64
// NumResolutions specifies the number of resolution levels.
// Default is 6 (5 decomposition levels + 1).
NumResolutions int
// CodeBlockSize specifies the code block dimensions (log2).
// Default is (6, 6) for 64x64 code blocks.
CodeBlockSize image.Point
// PrecinctSize specifies the precinct dimensions per resolution level.
// If nil, default sizes are used.
PrecinctSize []image.Point
// ProgressionOrder specifies the packet ordering.
ProgressionOrder ProgressionOrder
// NumLayers specifies the number of quality layers.
NumLayers int
// TileSize specifies the tile dimensions.
// If zero, the entire image is one tile.
TileSize image.Point
// TileOffset specifies the tile grid origin offset.
TileOffset image.Point
// ImageOffset specifies the image area offset.
ImageOffset image.Point
// ColorSpace specifies the color space.
ColorSpace ColorSpace
// ICCProfile specifies an optional ICC color profile.
ICCProfile []byte
// Comment specifies an optional comment string.
Comment string
// EnableSOP enables Start of Packet markers.
EnableSOP bool
// EnableEPH enables End of Packet Header markers.
EnableEPH bool
// ComponentSubsampling gives each component's XRsiz and YRsiz, the factors
// by which its grid is coarser than the image's reference grid. One entry
// per component; an empty list, or any entry of {0,0} or {1,1}, leaves that
// component at full resolution.
//
// {{1,1},{2,2},{2,2}} is 4:2:0 and {{1,1},{2,1},{2,1}} is 4:2:2. The
// samples of a subsampled component are taken by decimation — every
// XRsiz-th column of every YRsiz-th row — rather than by averaging, because
// the format specifies no filter and decimation is the one choice a
// decoder can invert exactly.
//
// The multi-component transform requires every component to share a grid,
// so it is not applied when the factors differ.
ComponentSubsampling []image.Point
// WritePacketLengths writes the packet length markers: PLT in every
// tile-part header and TLM in the main header.
//
// They change nothing about the image. What they change is the cost of
// finding a packet: without them a reader learns where packet N begins
// only by parsing packets 0 to N-1, which over a network is a chain of
// small dependent round trips. With them the offsets follow by summation
// from a few kilobytes near the front of the file, so a rolling prefetch
// across a frame sequence becomes one ranged read per frame.
//
// The cost is a few bytes per packet, and any conforming decoder that does
// not want them skips both markers.
WritePacketLengths bool
// PrecinctSizes gives the precinct partition, one entry per resolution
// from the lowest upward, as base-2 exponents of width and height. A
// shorter list repeats its last entry for the resolutions above it, and an
// empty list writes the maximal precinct, which is one packet per
// resolution and what the format assumes when Scod bit 0 is clear.
//
// Precincts are what make a codestream spatially addressable: without them
// a resolution is one packet covering the whole image, so a region of
// interest cannot be located without decoding everything. With them each
// packet covers one rectangle, and a packet index resolves a viewport to
// byte ranges.
//
// Each exponent must be in [1, 15], except that the lowest resolution may
// use 0. A precinct smaller than the code-block clips the code-block
// partition to it, which is legal and is how small precincts behave.
PrecinctSizes []PrecinctSize
// Precision overrides the bit depth for encoding.
// If 0, uses the natural precision of the input image (8 or 16).
// Valid values are 1-16. Values other than 8 or 16 will exercise
// precision scaling in the decoder.
Precision int
// HighThroughput enables HTJ2K (High-Throughput JPEG 2000) mode.
// When enabled, the FBCS (Fast Block Coding Stream) entropy coder
// is used instead of the standard MQ arithmetic coder.
// This provides significantly higher encoding/decoding throughput
// at a modest cost in compression efficiency.
HighThroughput bool
// HTBlockWidth specifies the code block width for HTJ2K mode.
// Valid values are 32 and 128. Default is 128.
// Only used when HighThroughput is true.
HTBlockWidth int
// HTBlockHeight specifies the code block height for HTJ2K mode.
// Valid values are 32 and 128. Default is 128.
// Only used when HighThroughput is true.
HTBlockHeight int
}
// DefaultOptions returns the default encoding options.
func DefaultOptions() *Options {
return &Options{
Format: FormatJP2,
Profile: ProfileNone,
Lossless: false,
Quality: 75,
NumResolutions: 6,
CodeBlockSize: image.Point{6, 6}, // 64x64
ProgressionOrder: LRCP,
NumLayers: 1,
}
}
// Decode reads a JPEG 2000 image from r and returns it as an image.Image.
func Decode(r io.Reader) (image.Image, error) {
return DecodeConfig(r, nil)
}
// DecodeCost reports what a decode spent: the bytes of code-block data it
// entropy-decoded, and the bytes it skipped because a decode area excluded
// them. Both are zero for a decode with no area.
//
// It exists so "a region costs less than the whole" is a measurement rather
// than a claim.
type DecodeCost struct {
Decoded int
Skipped int
// Resyncs is how many packets were recovered by scanning to the next SOP
// marker after a parse failure. Zero for an undamaged stream, and zero for
// any stream without SOP markers, where there is nothing to scan to.
Resyncs int
}
// DecodeConfigCost decodes with the given configuration and reports what the
// decode cost, for callers measuring the saving a region buys.
func DecodeConfigCost(r io.Reader, cfg *Config) (image.Image, DecodeCost, error) {
if err := checkConfig(cfg); err != nil {
return nil, DecodeCost{}, err
}
d := newDecoder(r)
img, err := d.decode(cfg)
return img, DecodeCost{Decoded: d.regionBytes, Skipped: d.skippedBytes, Resyncs: d.resyncs}, err
}
// DecodeConfig decodes a JPEG 2000 image with the specified configuration.
func DecodeConfig(r io.Reader, cfg *Config) (image.Image, error) {
if err := checkConfig(cfg); err != nil {
return nil, err
}
d := newDecoder(r)
return d.decode(cfg)
}
// checkConfig rejects a configuration this decoder cannot honour.
//
// A decoder that silently ignores an option is worse than one that lacks it: a
// caller asking for a 256-row region and receiving the whole image has no
// indication the request was dropped, and will read the wrong pixels from the
// buffer it sized for the answer it asked for. Nothing is refused here now,
// and the function stays as the one place to refuse from.
func checkConfig(cfg *Config) error {
return nil
}
// Encode writes the image m to w in JPEG 2000 format with the given options.
func Encode(w io.Writer, m image.Image, o *Options) error {
if o == nil {
o = DefaultOptions()
}
e := newEncoder(w, m, o)
return e.encode()
}
// EncodeFloat writes a FloatImage to w in JPEG 2000 format.
// Float encoding is always lossless (5/3 reversible wavelet).
// IEEE 754 float bits are reinterpreted as int32 and an NLT Type 3
// marker signals the codec to apply a sign-magnitude transform.
func EncodeFloat(w io.Writer, img *FloatImage, o *Options) error {
if o == nil {
o = DefaultOptions()
}
o.Lossless = true // float encoding is always lossless
e := &encoder{
w: w,
options: o,
floatImg: img,
}
return e.encodeFloat()
}
// EncodeHalf writes a HalfImage to w in JPEG 2000 format.
// Half encoding is always lossless (5/3 reversible wavelet): the IEEE 754
// binary16 bit patterns are reinterpreted as signed 16-bit samples and an NLT
// Type 3 marker with a 16-bit depth signals the decoder to apply the
// sign-magnitude transform at that width.
func EncodeHalf(w io.Writer, img *HalfImage, o *Options) error {
if o == nil {
o = DefaultOptions()
}
// Copy before forcing Lossless: o belongs to the caller, and reusing it for
// a later Encode must not silently inherit a setting made here.
opts := *o
opts.Lossless = true // half encoding is always lossless
e := &encoder{
w: w,
options: &opts,
halfImg: img,
}
return e.encodeHalf()
}
// DecodeHalf reads a JPEG 2000 image written by EncodeHalf and returns the
// exact binary16 bit patterns. It returns an error if the codestream does not
// carry 16-bit NLT Type 3 samples, rather than silently reinterpreting data
// that is not half float.
func DecodeHalf(r io.Reader) (*HalfImage, error) {
return DecodeHalfConfig(r, nil)
}
// DecodeHalfConfig decodes a half-float JPEG 2000 image with the specified
// configuration.
func DecodeHalfConfig(r io.Reader, cfg *Config) (*HalfImage, error) {
if err := checkConfig(cfg); err != nil {
return nil, err
}
d := newDecoder(r)
img, err := d.decodeHalf(cfg)
if img != nil {
img.Cost = DecodeCost{Decoded: d.regionBytes, Skipped: d.skippedBytes}
}
return img, err
}
// DecodeFloat reads a JPEG 2000 image and returns it as a FloatImage,
// preserving float precision from the wavelet transform pipeline.
func DecodeFloat(r io.Reader) (*FloatImage, error) {
return DecodeFloatConfig(r, nil)
}
// DecodeFloatConfig decodes a JPEG 2000 image with the specified configuration,
// returning a FloatImage that preserves float precision.
func DecodeFloatConfig(r io.Reader, cfg *Config) (*FloatImage, error) {
if err := checkConfig(cfg); err != nil {
return nil, err
}
d := newDecoder(r)
img, err := d.decodeFloat(cfg)
if img != nil {
img.Cost = DecodeCost{Decoded: d.regionBytes, Skipped: d.skippedBytes}
}
return img, err
}
// DecodeMetadata reads only the header information without decoding the image.
func DecodeMetadata(r io.Reader) (*Metadata, error) {
d := newDecoder(r)
return d.readMetadata()
}
// Metadata contains image metadata extracted from the JPEG 2000 file.
type Metadata struct {
// Format is the detected file format.
Format Format
// Width is the image width in pixels.
Width int
// Height is the image height in pixels.
Height int
// NumComponents is the number of color components.
NumComponents int
// BitsPerComponent is the bit depth for each component.
BitsPerComponent []int
// Signed indicates whether each component uses signed values.
Signed []bool
// ColorSpace is the detected color space.
ColorSpace ColorSpace
// Profile is the JPEG 2000 profile.
Profile Profile
// NumResolutions is the number of resolution levels.
NumResolutions int
// NumQualityLayers is the number of quality layers.
NumQualityLayers int
// TileWidth is the tile width.
TileWidth int
// TileHeight is the tile height.
TileHeight int
// NumTilesX is the number of tiles horizontally.
NumTilesX int
// NumTilesY is the number of tiles vertically.
NumTilesY int
// ICCProfile is the embedded ICC color profile, if any.
ICCProfile []byte
// Comment is the embedded comment string, if any.
Comment string
}
// init registers the JPEG 2000 format with the image package.
func init() {
// Register JP2 format (with signature box)
image.RegisterFormat("jp2",
"\x00\x00\x00\x0cjP \r\n\x87\n",
func(r io.Reader) (image.Image, error) {
return Decode(r)
},
func(r io.Reader) (image.Config, error) {
m, err := DecodeMetadata(r)
if err != nil {
return image.Config{}, err
}
return image.Config{
Width: m.Width,
Height: m.Height,
}, nil
})
// Register J2K format (raw codestream)
image.RegisterFormat("j2k",
"\xff\x4f\xff\x51",
func(r io.Reader) (image.Image, error) {
return Decode(r)
},
func(r io.Reader) (image.Config, error) {
m, err := DecodeMetadata(r)
if err != nil {
return image.Config{}, err
}
return image.Config{
Width: m.Width,
Height: m.Height,
}, nil
})
}