saliacoel commited on
Commit
5153db5
·
verified ·
1 Parent(s): 0498eff

Upload BAM_img_to_bam.py

Browse files
Files changed (1) hide show
  1. BAM_img_to_bam.py +203 -0
BAM_img_to_bam.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import re
5
+ from typing import Any, List
6
+
7
+ from PIL import Image, ExifTags
8
+ import folder_paths
9
+
10
+
11
+ _BAM_RE = re.compile(
12
+ r"GPT_BAM_START###.*?###GPT_BAM_END",
13
+ flags=re.IGNORECASE | re.DOTALL,
14
+ )
15
+
16
+ _IMAGE_EXTS = {
17
+ ".png",
18
+ ".jpg",
19
+ ".jpeg",
20
+ ".webp",
21
+ ".bmp",
22
+ ".tif",
23
+ ".tiff",
24
+ ".gif",
25
+ }
26
+
27
+
28
+ def _safe_decode_bytes(data: bytes) -> str:
29
+ if not data:
30
+ return ""
31
+
32
+ # EXIF UserComment prefixes
33
+ prefixes = [
34
+ b"ASCII\x00\x00\x00",
35
+ b"UNICODE\x00",
36
+ b"JIS\x00\x00\x00\x00\x00",
37
+ ]
38
+ for p in prefixes:
39
+ if data.startswith(p):
40
+ data = data[len(p):]
41
+ break
42
+
43
+ for enc in ("utf-8", "utf-16", "utf-16le", "latin-1"):
44
+ try:
45
+ return data.decode(enc, errors="ignore")
46
+ except Exception:
47
+ pass
48
+
49
+ return ""
50
+
51
+
52
+ def _collect_strings(value: Any, out: List[str]) -> None:
53
+ if value is None:
54
+ return
55
+
56
+ if isinstance(value, str):
57
+ if value:
58
+ out.append(value)
59
+ return
60
+
61
+ if isinstance(value, bytes):
62
+ decoded = _safe_decode_bytes(value)
63
+ if decoded:
64
+ out.append(decoded)
65
+ return
66
+
67
+ if isinstance(value, dict):
68
+ for k, v in value.items():
69
+ if isinstance(k, str) and k:
70
+ out.append(k)
71
+ _collect_strings(v, out)
72
+ return
73
+
74
+ if isinstance(value, (list, tuple, set)):
75
+ for item in value:
76
+ _collect_strings(item, out)
77
+ return
78
+
79
+ # Ignore plain numbers / objects by default
80
+ return
81
+
82
+
83
+ def _extract_from_text_blob(text: str) -> str:
84
+ if not text:
85
+ return ""
86
+ m = _BAM_RE.search(text)
87
+ if not m:
88
+ return ""
89
+ return m.group(0).replace("\x00", "").strip()
90
+
91
+
92
+ def _extract_from_pil_metadata(image_path: str) -> str:
93
+ text_parts: List[str] = []
94
+
95
+ with Image.open(image_path) as img:
96
+ # Standard PIL info dict
97
+ _collect_strings(getattr(img, "info", {}), text_parts)
98
+
99
+ # PNG text chunks if present
100
+ if hasattr(img, "text"):
101
+ _collect_strings(getattr(img, "text"), text_parts)
102
+
103
+ # EXIF
104
+ try:
105
+ exif = img.getexif()
106
+ if exif:
107
+ named_exif = {}
108
+ for tag_id, value in exif.items():
109
+ tag_name = ExifTags.TAGS.get(tag_id, str(tag_id))
110
+ named_exif[tag_name] = value
111
+ _collect_strings(named_exif, text_parts)
112
+ except Exception:
113
+ pass
114
+
115
+ combined = "\n".join(text_parts)
116
+ return _extract_from_text_blob(combined)
117
+
118
+
119
+ def _extract_from_raw_file(image_path: str) -> str:
120
+ try:
121
+ with open(image_path, "rb") as f:
122
+ raw = f.read()
123
+ except Exception:
124
+ return ""
125
+
126
+ # Try direct byte search first
127
+ try:
128
+ start = raw.find(b"GPT_BAM_START###")
129
+ if start != -1:
130
+ end_marker = b"###GPT_BAM_END"
131
+ end = raw.find(end_marker, start)
132
+ if end != -1:
133
+ end += len(end_marker)
134
+ found = raw[start:end].decode("utf-8", errors="ignore").replace("\x00", "").strip()
135
+ if found:
136
+ return found
137
+ except Exception:
138
+ pass
139
+
140
+ # Decode whole file with a few encodings and regex-search
141
+ for enc in ("utf-8", "utf-16", "utf-16le", "latin-1"):
142
+ try:
143
+ text = raw.decode(enc, errors="ignore")
144
+ except Exception:
145
+ continue
146
+
147
+ found = _extract_from_text_blob(text)
148
+ if found:
149
+ return found
150
+
151
+ return ""
152
+
153
+
154
+ class img_meta_to_BAM:
155
+ @classmethod
156
+ def INPUT_TYPES(cls):
157
+ input_dir = folder_paths.get_input_directory()
158
+ files = []
159
+
160
+ if os.path.isdir(input_dir):
161
+ for f in os.listdir(input_dir):
162
+ full = os.path.join(input_dir, f)
163
+ if os.path.isfile(full):
164
+ ext = os.path.splitext(f)[1].lower()
165
+ if ext in _IMAGE_EXTS:
166
+ files.append(f)
167
+
168
+ return {
169
+ "required": {
170
+ "image": (sorted(files), {"image_upload": True}),
171
+ }
172
+ }
173
+
174
+ RETURN_TYPES = ("STRING",)
175
+ RETURN_NAMES = ("BAM_format",)
176
+ FUNCTION = "extract_bam"
177
+ CATEGORY = "BAM"
178
+
179
+ def extract_bam(self, image):
180
+ image_path = folder_paths.get_annotated_filepath(image)
181
+
182
+ bam = ""
183
+
184
+ # First try proper metadata extraction
185
+ try:
186
+ bam = _extract_from_pil_metadata(image_path)
187
+ except Exception:
188
+ bam = ""
189
+
190
+ # Fallback: raw file scan
191
+ if not bam:
192
+ bam = _extract_from_raw_file(image_path)
193
+
194
+ return (bam,)
195
+
196
+
197
+ NODE_CLASS_MAPPINGS = {
198
+ "img_meta_to_BAM": img_meta_to_BAM,
199
+ }
200
+
201
+ NODE_DISPLAY_NAME_MAPPINGS = {
202
+ "img_meta_to_BAM": "img_meta_to_BAM",
203
+ }