or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

decoding.mdencoding.mdindex.mdtransformations.mdutilities.md

utilities.mddocs/

0

# Utility Functions

1

2

Buffer size calculation, scaling factor information, and helper utilities for optimizing memory usage, determining available scaling options, and working with JPEG processing parameters.

3

4

## Capabilities

5

6

### Buffer Size Calculation

7

8

Calculate the maximum buffer size needed for JPEG encoding operations to optimize memory allocation.

9

10

```python { .api }

11

def buffer_size(img_array: np.ndarray, jpeg_subsample: int = TJSAMP_422) -> int:

12

"""

13

Calculate maximum number of bytes of compressed JPEG data.

14

15

Args:

16

img_array: Input image array

17

jpeg_subsample: JPEG subsampling type (TJSAMP_* constants)

18

19

Returns:

20

int: Maximum buffer size needed in bytes

21

22

Notes:

23

- Use this for pre-allocating buffers for in-place encoding

24

- Actual encoded size will typically be smaller

25

- Calculation accounts for worst-case compression scenario

26

"""

27

```

28

29

### Scaling Factor Information

30

31

Access available scaling factors supported by the libjpeg-turbo library for decode operations.

32

33

```python { .api }

34

@property

35

def scaling_factors(self) -> frozenset[tuple[int, int]]:

36

"""

37

Available scaling factors for decode operations.

38

39

Returns:

40

frozenset: Set of (numerator, denominator) tuples representing valid scaling factors

41

42

Notes:

43

- Common factors include (1,8), (1,4), (1,2), (1,1), (2,1) etc.

44

- Use these for efficient decode-time scaling

45

- Scaling happens during JPEG decompression for better performance

46

"""

47

```

48

49

## Constants and Arrays

50

51

### Pixel Size Information

52

53

```python { .api }

54

tjPixelSize: list[int] = [3, 3, 4, 4, 4, 4, 1, 4, 4, 4, 4, 4] # Bytes per pixel for each pixel format

55

# Index corresponds to TJPF_* constants:

56

# TJPF_RGB=0: 3 bytes, TJPF_BGR=1: 3 bytes, TJPF_RGBX=2: 4 bytes,

57

# TJPF_BGRX=3: 4 bytes, TJPF_XBGR=4: 4 bytes, TJPF_XRGB=5: 4 bytes,

58

# TJPF_GRAY=6: 1 byte, TJPF_RGBA=7: 4 bytes, TJPF_BGRA=8: 4 bytes,

59

# TJPF_ABGR=9: 4 bytes, TJPF_ARGB=10: 4 bytes, TJPF_CMYK=11: 4 bytes

60

```

61

62

### MCU Dimensions

63

64

```python { .api }

65

tjMCUWidth: list[int] = [8, 16, 16, 8, 8, 32] # MCU width in pixels for each subsampling type

66

tjMCUHeight: list[int] = [8, 8, 16, 8, 16, 8] # MCU height in pixels for each subsampling type

67

68

# MCU sizes by subsampling index:

69

# TJSAMP_444=0: 8x8, TJSAMP_422=1: 16x8, TJSAMP_420=2: 16x16

70

# TJSAMP_GRAY=3: 8x8, TJSAMP_440=4: 8x16, TJSAMP_411=5: 32x8

71

```

72

73

### Error Constants

74

75

```python { .api }

76

TJERR_WARNING: int = 0 # Warning error level

77

TJERR_FATAL: int = 1 # Fatal error level

78

```

79

80

### MCU Constants

81

82

```python { .api }

83

MCU_WIDTH: int = 8 # Base MCU width in pixels

84

MCU_HEIGHT: int = 8 # Base MCU height in pixels

85

MCU_SIZE: int = 64 # Base MCU size in pixels (8x8)

86

```

87

88

### Platform Library Paths

89

90

```python { .api }

91

DEFAULT_LIB_PATHS: dict[str, list[str]] # Default libjpeg-turbo library paths by platform

92

93

# Platform-specific paths:

94

# 'Darwin': macOS paths (/usr/local/opt/jpeg-turbo/lib/, /opt/homebrew/opt/jpeg-turbo/lib/)

95

# 'Linux': Linux paths (/usr/lib/x86_64-linux-gnu/, /usr/lib64/, etc.)

96

# 'Windows': Windows paths (C:/libjpeg-turbo64/bin/)

97

# 'FreeBSD': FreeBSD paths (/usr/local/lib/)

98

# 'NetBSD': NetBSD paths (/usr/pkg/lib/)

99

```

100

101

## Utility Functions

102

103

### Nibble Operations

104

105

```python { .api }

106

def split_byte_into_nibbles(value: int) -> tuple[int, int]:

107

"""

108

Split byte into two 4-bit nibbles.

109

110

Args:

111

value: Byte value (0-255)

112

113

Returns:

114

tuple[int, int]: (first_nibble, second_nibble) where each is 0-15

115

"""

116

```

117

118

## Usage Examples

119

120

### Buffer Size Calculation

121

122

```python

123

import numpy as np

124

from turbojpeg import TurboJPEG, TJSAMP_420, TJSAMP_444

125

126

jpeg = TurboJPEG()

127

128

# Create sample image

129

image = np.random.randint(0, 256, (1080, 1920, 3), dtype=np.uint8)

130

131

# Calculate buffer sizes for different subsampling

132

size_422 = jpeg.buffer_size(image) # Default TJSAMP_422

133

size_420 = jpeg.buffer_size(image, TJSAMP_420)

134

size_444 = jpeg.buffer_size(image, TJSAMP_444)

135

136

print(f"Image shape: {image.shape}")

137

print(f"Buffer size 4:2:2: {size_422:,} bytes")

138

print(f"Buffer size 4:2:0: {size_420:,} bytes")

139

print(f"Buffer size 4:4:4: {size_444:,} bytes")

140

141

# Pre-allocate buffer for in-place encoding

142

buffer = bytearray(size_422)

143

encoded_data, actual_size = jpeg.encode(image, dst=buffer)

144

145

print(f"Actual encoded size: {actual_size:,} bytes")

146

print(f"Buffer utilization: {actual_size/size_422*100:.1f}%")

147

```

148

149

### Scaling Factor Usage

150

151

```python

152

# Check all available scaling factors

153

print("Available scaling factors:")

154

for num, denom in sorted(jpeg.scaling_factors):

155

scale = num / denom

156

print(f" {num}/{denom} = {scale:.3f}x")

157

158

# Common scaling factors and their uses

159

common_scales = {

160

(1, 8): "1/8 scale - tiny thumbnails",

161

(1, 4): "1/4 scale - small thumbnails",

162

(1, 2): "1/2 scale - medium thumbnails",

163

(1, 1): "1:1 scale - original size",

164

(2, 1): "2x scale - upscaling"

165

}

166

167

print("\nCommon uses:")

168

for factor, description in common_scales.items():

169

if factor in jpeg.scaling_factors:

170

print(f" {factor[0]}/{factor[1]}: {description}")

171

```

172

173

### Efficient Thumbnail Generation

174

175

```python

176

def generate_thumbnails(jpeg_data, sizes):

177

"""Generate multiple thumbnail sizes efficiently."""

178

179

jpeg = TurboJPEG()

180

thumbnails = {}

181

182

# Get available scaling factors

183

available_factors = jpeg.scaling_factors

184

185

for name, target_size in sizes.items():

186

# Find best scaling factor

187

best_factor = None

188

best_scale = float('inf')

189

190

for num, denom in available_factors:

191

scale = num / denom

192

if 0.1 <= scale <= 1.0: # Only downscaling factors

193

if abs(scale - target_size) < abs(best_scale - target_size):

194

best_factor = (num, denom)

195

best_scale = scale

196

197

if best_factor:

198

# Decode at reduced scale

199

thumbnail = jpeg.decode(

200

jpeg_data,

201

scaling_factor=best_factor

202

)

203

thumbnails[name] = thumbnail

204

print(f"{name}: using {best_factor[0]}/{best_factor[1]} = {best_scale:.3f}x")

205

else:

206

# Fallback to full decode + resize

207

print(f"{name}: no suitable scaling factor, using full decode")

208

209

return thumbnails

210

211

# Usage

212

with open('photo.jpg', 'rb') as f:

213

photo_data = f.read()

214

215

thumbnail_sizes = {

216

'tiny': 0.125, # 1/8 scale

217

'small': 0.25, # 1/4 scale

218

'medium': 0.5, # 1/2 scale

219

}

220

221

thumbs = generate_thumbnails(photo_data, thumbnail_sizes)

222

```

223

224

### Memory Optimization

225

226

```python

227

def encode_with_optimal_buffer(image_array, quality=85):

228

"""Encode with pre-calculated optimal buffer size."""

229

230

jpeg = TurboJPEG()

231

232

# Calculate exact buffer size needed

233

max_size = jpeg.buffer_size(image_array)

234

235

# Allocate buffer

236

buffer = bytearray(max_size)

237

238

# Encode in-place

239

result_buffer, actual_size = jpeg.encode(

240

image_array,

241

quality=quality,

242

dst=buffer

243

)

244

245

# Return only the used portion

246

return bytes(buffer[:actual_size])

247

248

# Compare with standard encoding

249

image = np.random.randint(0, 256, (480, 640, 3), dtype=np.uint8)

250

251

# Standard encoding (allocates internally)

252

standard_result = jpeg.encode(image)

253

254

# Optimized encoding (pre-allocated buffer)

255

optimal_result = encode_with_optimal_buffer(image)

256

257

print(f"Standard result size: {len(standard_result)} bytes")

258

print(f"Optimal result size: {len(optimal_result)} bytes")

259

print(f"Results identical: {standard_result == optimal_result}")

260

```

261

262

### Platform-Specific Library Loading

263

264

```python

265

import platform

266

from turbojpeg import TurboJPEG, DEFAULT_LIB_PATHS

267

268

def create_jpeg_processor():

269

"""Create TurboJPEG processor with platform-specific fallbacks."""

270

271

# Try default initialization first

272

try:

273

return TurboJPEG()

274

except RuntimeError as e:

275

print(f"Default initialization failed: {e}")

276

277

# Try platform-specific paths

278

system = platform.system()

279

if system in DEFAULT_LIB_PATHS:

280

for lib_path in DEFAULT_LIB_PATHS[system]:

281

try:

282

print(f"Trying {lib_path}...")

283

return TurboJPEG(lib_path=lib_path)

284

except (RuntimeError, OSError):

285

continue

286

287

# Final fallback - let user specify

288

raise RuntimeError(

289

"Could not locate libjpeg-turbo library. "

290

"Please install libjpeg-turbo or specify lib_path manually."

291

)

292

293

# Usage

294

try:

295

jpeg = create_jpeg_processor()

296

print("TurboJPEG initialized successfully")

297

print(f"Available scaling factors: {len(jpeg.scaling_factors)}")

298

except RuntimeError as e:

299

print(f"Failed to initialize: {e}")

300

```

301

302

### Working with MCU Alignment

303

304

```python

305

def get_mcu_aligned_crop(width, height, subsample, x, y, w, h):

306

"""Calculate MCU-aligned crop coordinates."""

307

308

from turbojpeg import tjMCUWidth, tjMCUHeight

309

310

mcu_w = tjMCUWidth[subsample]

311

mcu_h = tjMCUHeight[subsample]

312

313

# Align to MCU boundaries

314

aligned_x = (x // mcu_w) * mcu_w

315

aligned_y = (y // mcu_h) * mcu_h

316

aligned_w = ((w + mcu_w - 1) // mcu_w) * mcu_w

317

aligned_h = ((h + mcu_h - 1) // mcu_h) * mcu_h

318

319

# Ensure within image bounds

320

aligned_w = min(aligned_w, width - aligned_x)

321

aligned_h = min(aligned_h, height - aligned_y)

322

323

return aligned_x, aligned_y, aligned_w, aligned_h

324

325

# Usage

326

jpeg = TurboJPEG()

327

328

with open('image.jpg', 'rb') as f:

329

jpeg_data = f.read()

330

331

# Get image properties

332

width, height, subsample, _ = jpeg.decode_header(jpeg_data)

333

334

# Desired crop

335

desired_crop = (100, 150, 300, 200)

336

337

# Get MCU-aligned version

338

aligned_crop = get_mcu_aligned_crop(

339

width, height, subsample, *desired_crop

340

)

341

342

print(f"Desired crop: {desired_crop}")

343

print(f"MCU-aligned crop: {aligned_crop}")

344

print(f"Subsampling: {subsample} (MCU: {tjMCUWidth[subsample]}x{tjMCUHeight[subsample]})")

345

346

# Perform aligned crop (fast)

347

cropped = jpeg.crop(jpeg_data, *aligned_crop)

348

```