Text-to-Image
Diffusers
Safetensors
English
Chinese
QwenImage21Pipeline
bitsandbytes
int8
image-generation
image-editing
rgba
8-bit precision
Instructions to use ixim/Image21-INT8 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use ixim/Image21-INT8 with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("ixim/Image21-INT8", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Draw Things
- DiffusionBee
File size: 8,655 Bytes
6f8c80a ba48d54 6f8c80a 1435032 6f8c80a ba48d54 6f8c80a | 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 | """Compare paired inference runs; preserve raw outputs and derive honest metrics."""
import argparse
import csv
import html
import json
import math
import statistics
from pathlib import Path
import numpy as np
from PIL import Image
from scripts.integrity import sha256, validate_pair
from scripts.provenance import validate_roles
def load_records(root):
rows = [json.loads(line) for line in (root / 'records.jsonl').read_text(encoding='utf-8').splitlines() if line]
records = {}
for row in rows:
key = (row['case_id'], row['seed'])
if key in records:
raise ValueError(f'Duplicate record: {key}')
if sha256(root / row['image']) != row['image_sha256']:
raise ValueError(f'Image hash mismatch: {key}')
records[key] = row
if not records:
raise ValueError('Empty benchmark run')
return records
def compare_pixels(a_path, b_path):
with Image.open(a_path) as a, Image.open(b_path) as b:
if a.size != b.size:
raise ValueError('Image dimensions differ')
a, b = a.convert('RGBA'), b.convert('RGBA')
aa, bb = np.asarray(a, dtype=np.float32) / 255, np.asarray(b, dtype=np.float32) / 255
# White compositing avoids invisible RGB values dominating a transparency comparison.
ac = aa[..., :3] * aa[..., 3:] + 1 - aa[..., 3:]
bc = bb[..., :3] * bb[..., 3:] + 1 - bb[..., 3:]
mse = float(np.mean((ac - bc) ** 2))
return {'rgb_mae_white': float(np.mean(np.abs(ac - bc))),
'rgb_psnr_white_db': None if mse == 0 else -10 * math.log10(mse),
'alpha_mae': float(np.mean(np.abs(aa[..., 3] - bb[..., 3])))}
def main():
ap = argparse.ArgumentParser()
ap.add_argument('--baseline', default='artifacts/eval/bf16')
ap.add_argument('--quantized', default='artifacts/eval/int8')
ap.add_argument('--output', default='artifacts/eval')
args = ap.parse_args()
base, quant, out = Path(args.baseline), Path(args.quantized), Path(args.output)
a, b = load_records(base), load_records(quant)
if a.keys() != b.keys():
raise ValueError('Benchmark cases/seeds are incomplete or mismatched')
ea = json.loads((base / 'environment.json').read_text())
eb = json.loads((quant / 'environment.json').read_text())
validate_roles(ea['model_identity'], eb['model_identity'])
for key in ('offload_aux_fix', 'benchmark_sha256', 'runtime_helper_sha256'):
if ea.get(key) != eb.get(key):
raise ValueError(f'Benchmark implementation mismatch: {key}; report diagnostic runs separately')
for key in ('gpu', 'cuda', 'packages', 'offload', 'warmup', 'generator_device', 'cases_sha256'):
if ea[key] != eb[key]:
raise ValueError(f'Runtime mismatch: {key}')
rows = []
for key in a:
x, y = a[key], b[key]
validate_pair(x, y)
rows.append({'case_id': key[0], 'seed': key[1],
'bf16_seconds': x['seconds'], 'int8_seconds': y['seconds'],
'speed_ratio_bf16_over_int8': x['seconds'] / y['seconds'],
'bf16_peak_allocated_gib': x['peak_allocated_bytes'] / 2**30,
'int8_peak_allocated_gib': y['peak_allocated_bytes'] / 2**30,
**compare_pixels(base / x['image'], quant / y['image'])})
summary = {'pairs': len(rows), 'cases': len({r['case_id'] for r in rows}),
'bf16_mean_seconds': statistics.mean(r['bf16_seconds'] for r in rows),
'int8_mean_seconds': statistics.mean(r['int8_seconds'] for r in rows),
'bf16_max_allocated_gib': max(r['bf16_peak_allocated_gib'] for r in rows),
'int8_max_allocated_gib': max(r['int8_peak_allocated_gib'] for r in rows),
'width': next(iter(a.values()))['width'], 'height': next(iter(a.values()))['height'],
'steps': next(iter(a.values()))['steps'], 'warmup': ea['warmup'],
'bf16_weight_bytes': sum(f['size'] for f in ea['model_identity']['files'] if f['path'].endswith('.safetensors')),
'int8_weight_bytes': sum(f['size'] for f in eb['model_identity']['files'] if f['path'].endswith('.safetensors'))}
out.mkdir(parents=True, exist_ok=True)
(out / 'summary.json').write_text(json.dumps(summary, indent=2), encoding='utf-8')
with (out / 'comparison.csv').open('w', encoding='utf-8', newline='') as f:
writer = csv.DictWriter(f, fieldnames=list(rows[0]))
writer.writeheader()
writer.writerows(rows)
lines = ['# Informal release evaluation: BF16 / INT8 comparison', '',
'This community evaluation is provided for reference only and does not represent any official evaluation.', '',
f'{len(rows)} paired outputs on {ea["gpu"]}; {summary["width"]}×{summary["height"]}, '
f'{summary["steps"]} steps, offload={ea["offload"]}, CFG=1, KV cache enabled.', '',
('Warmup excluded. ' if ea['warmup'] else 'No warmup performed. ') +
'Pixel metrics measure drift, not semantic quality. ' +
('The same BF16 portrait is used as input for every editing pair. ' if any(k[0] == 'edit' for k in a) else '') +
'The suite is small and does not establish a general quality ranking.', '',
'| Case | Seed | BF16 s | INT8 s | BF16 peak GiB | INT8 peak GiB | RGB MAE (white) |',
'|---|---:|---:|---:|---:|---:|---:|']
gallery = ['<!doctype html><meta charset="utf-8"><title>BF16 / INT8 comparison</title>',
'<style>body{font:16px system-ui;margin:32px;max-width:1500px}section{margin:40px 0}'
'.pair{display:grid;grid-template-columns:1fr 1fr;gap:20px}img{width:100%;background:'
'repeating-conic-gradient(#ddd 0 25%,#fff 0 50%) 0/24px 24px}pre{white-space:pre-wrap}</style>',
'<h1>BF16 / INT8 comparison</h1><p>Left: BF16. Right: saved INT8. '
'Identical settings and seeds. Alpha displayed over checkerboard.</p>']
if (out / 'young_woman/summary.json').exists():
lines[2:2] = ['Additional adult Chinese woman portrait pairs are reported separately: '
'[supplement](young_woman/report.md).', '']
gallery.append('<p><a href="young_woman/comparison.html">Additional adult Chinese woman portrait pairs</a></p>')
for r in rows:
lines.append(f'| {r["case_id"]} | {r["seed"]} | {r["bf16_seconds"]:.2f} | '
f'{r["int8_seconds"]:.2f} | {r["bf16_peak_allocated_gib"]:.2f} | '
f'{r["int8_peak_allocated_gib"]:.2f} | {r["rgb_mae_white"]:.4f} |')
key = (r['case_id'], r['seed'])
import os
left = Path(os.path.relpath(base / a[key]['image'], out)).as_posix()
right = Path(os.path.relpath(quant / b[key]['image'], out)).as_posix()
gallery.append(f'<section><h2>{html.escape(r["case_id"])} — seed {r["seed"]}</h2>'
f'<pre>{html.escape(a[key]["prompt"])}</pre><div class="pair">'
f'<a href="{html.escape(left, quote=True)}"><img src="{html.escape(left, quote=True)}" alt="BF16"></a>'
f'<a href="{html.escape(right, quote=True)}"><img src="{html.escape(right, quote=True)}" alt="INT8"></a></div></section>')
lines.extend(['', '## Summary', '',
f'Mean latency: BF16 {summary["bf16_mean_seconds"]:.2f}s; '
f'INT8 {summary["int8_mean_seconds"]:.2f}s.', '',
'[Interactive-sized side-by-side gallery](comparison.html). '
'Raw data: comparison.csv, bf16/records.jsonl, int8/records.jsonl. '
'Environment records include package versions and loading overhead.', '',
'[Qualitative observations](qualitative.md).', '',
'## Interpretation limits', '',
'- No CLIP, OCR, human preference, FID or benchmark leaderboard score is claimed.',
'- Peak CUDA allocated/reserved memory excludes other processes and display usage.',
'- Measured call latency includes transfers; disk writing and model loading are excluded.',
'- Paired images may diverge with quantization even when both remain plausible.',
'- Editing and transparency should be inspected in the gallery, including preserved details.'])
(out / 'report.md').write_text('\n'.join(lines) + '\n', encoding='utf-8')
(out / 'comparison.html').write_text('\n'.join(gallery), encoding='utf-8')
print(json.dumps(summary, indent=2))
if __name__ == '__main__':
main()
|