nt25 0.1.6__tar.gz → 0.1.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,9 +17,10 @@ uv.lock
17
17
  ds/
18
18
 
19
19
  # output
20
+ out.csv
20
21
  output/
21
22
 
22
- # others
23
- tests/*.jpg
24
- tests/*.png
23
+ # test
24
+ tests/**/*.jpg
25
+ tests/**/*.png
25
26
 
@@ -1,13 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nt25
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary: Neo's Tools of Python
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: exif>=1.6.1
7
7
  Requires-Dist: matplotlib>=3.10.6
8
8
  Requires-Dist: openpyxl>=3.1.5
9
9
  Requires-Dist: pandas>=2.3.2
10
+ Requires-Dist: pillow>=11.3.0
10
11
  Requires-Dist: pyinstaller>=6.15.0
12
+ Requires-Dist: requests>=2.32.5
11
13
  Requires-Dist: scikit-learn>=1.7.1
12
14
  Requires-Dist: sympy>=1.14.0
13
15
  Description-Content-Type: text/markdown
@@ -22,10 +24,10 @@ Neo's Tools of Python in 2025
22
24
  - [x] calc
23
25
  - [x] draw
24
26
  - [x] et: EXIF tools
27
+ - [x] mt: MapXYZ Transfer tool
28
+ - [x] ttp: TIFF to PNG, thread support
25
29
  - [ ] mysql
26
30
  - [ ] redis
27
- - [ ] maptrans
28
- - [ ] ttp
29
31
 
30
32
  ## scripts from init
31
33
 
@@ -8,10 +8,10 @@ Neo's Tools of Python in 2025
8
8
  - [x] calc
9
9
  - [x] draw
10
10
  - [x] et: EXIF tools
11
+ - [x] mt: MapXYZ Transfer tool
12
+ - [x] ttp: TIFF to PNG, thread support
11
13
  - [ ] mysql
12
14
  - [ ] redis
13
- - [ ] maptrans
14
- - [ ] ttp
15
15
 
16
16
  ## scripts from init
17
17
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nt25"
3
- version = "0.1.6"
3
+ version = "0.1.8"
4
4
  description = "Neo's Tools of Python"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -12,11 +12,15 @@ dependencies = [
12
12
  "openpyxl>=3.1.5",
13
13
  "scikit-learn>=1.7.1",
14
14
  "sympy>=1.14.0",
15
+ "pillow>=11.3.0",
16
+ "requests>=2.32.5",
15
17
  ]
16
18
 
17
19
  [project.scripts]
18
20
  demo = "nt25.demo:main"
19
21
  et = "nt25.lib.et:main"
22
+ mt = "nt25.mt:main"
23
+ ttp = "nt25.ttp:main"
20
24
 
21
25
  [build-system]
22
26
  requires = ["hatchling"]
@@ -1,14 +1,16 @@
1
- import io
2
- from operator import length_hint
1
+ import os
3
2
  import time
4
3
  import json
5
4
  import struct
6
5
  import argparse
7
6
 
7
+ from PIL import Image as pi
8
+
8
9
  from datetime import UTC, datetime, timedelta, timezone
9
10
  from exif import Image, DATETIME_STR_FORMAT
10
11
 
11
- VERSION = "0.1.2"
12
+ VERSION = "0.1.4"
13
+ MAX_WIDTH = 1080
12
14
  COMMENT_SEGMENT = b"\xff\xfe"
13
15
  EPOCH = datetime.fromtimestamp(0, UTC)
14
16
 
@@ -32,6 +34,27 @@ def gpsDt2Dt(date, time, offset=8):
32
34
  return utc.astimezone(timezone(timedelta(hours=offset)))
33
35
 
34
36
 
37
+ def optimizeFile(file, q=80, mw=MAX_WIDTH):
38
+ less = False
39
+ ofile = file + '.jpg'
40
+
41
+ img = pi.open(file)
42
+ w, h = img.size
43
+ scale = mw / max(w, h)
44
+ w = int(scale * w)
45
+ h = int(scale * h)
46
+ img.resize((w, h)).save(ofile, quality=q, optimize=True, progression=True)
47
+
48
+ if os.path.getsize(ofile) < os.path.getsize(file) * 0.8:
49
+ less = True
50
+ transplant(file, ofile)
51
+ os.replace(ofile, file)
52
+ else:
53
+ os.remove(ofile)
54
+
55
+ return less
56
+
57
+
35
58
  def tryGet(img, key, default):
36
59
  value = default
37
60
 
@@ -43,9 +66,12 @@ def tryGet(img, key, default):
43
66
  return value
44
67
 
45
68
 
46
- def dumpExif(file):
69
+ def dumpExif(file, optimize=False):
47
70
  result = {}
48
71
 
72
+ if optimize:
73
+ optimizeFile(file)
74
+
49
75
  with open(file, 'rb') as f:
50
76
  img = Image(f)
51
77
  for key in img.get_all():
@@ -57,58 +83,74 @@ def dumpExif(file):
57
83
  return result
58
84
 
59
85
 
60
- def parseExif(file):
86
+ def parseExif(file, optimize=False):
87
+ if optimize:
88
+ optimizeFile(file)
89
+
61
90
  with open(file, 'rb') as f:
62
91
  try:
63
92
  img = Image(f)
64
93
  except Exception:
65
94
  return {}
66
95
 
67
- width = tryGet(img, 'pixel_x_dimension', -1)
68
- height = tryGet(img, 'pixel_y_dimension', -1)
96
+ width = tryGet(img, 'pixel_x_dimension', -1)
97
+ height = tryGet(img, 'pixel_y_dimension', -1)
98
+
99
+ if width < 0:
100
+ width = tryGet(img, 'image_width', -1)
101
+ height = tryGet(img, 'image_height', -1)
102
+
103
+ create = tryGet(img, 'datetime_original', None)
104
+ modify = tryGet(img, 'datetime', None)
69
105
 
70
- if width < 0:
71
- width = tryGet(img, 'image_width', -1)
72
- height = tryGet(img, 'image_height', -1)
106
+ createDt = None if create is None else dtFormatter(create)
107
+ modifyDt = None if modify is None else dtFormatter(modify)
73
108
 
74
- create = tryGet(img, 'datetime_original', None)
75
- modify = tryGet(img, 'datetime', None)
109
+ latitude = tryGet(img, 'gps_latitude', None)
110
+ latitude = None if latitude is None else dms2dec(latitude)
76
111
 
77
- createDt = None if create is None else dtFormatter(create)
78
- modifyDt = None if modify is None else dtFormatter(modify)
112
+ latRef = tryGet(img, "gps_latitude_ref", default='N')
113
+ if latRef != 'N' and latitude:
114
+ latitude = -latitude
79
115
 
80
- latitude = tryGet(img, 'gps_latitude', None)
81
- latitude = None if latitude is None else dms2dec(latitude)
116
+ longitude = tryGet(img, 'gps_longitude', None)
117
+ longitude = None if longitude is None else dms2dec(longitude)
82
118
 
83
- longitude = tryGet(img, 'gps_longitude', None)
84
- longitude = None if longitude is None else dms2dec(longitude)
119
+ longRef = tryGet(img, "gps_longitude_ref", default='E')
120
+ if longRef != 'E' and longitude:
121
+ longitude = -longitude
85
122
 
86
- gpsDatetime = None
87
- gd = tryGet(img, 'gps_datestamp', None)
88
- gt = tryGet(img, 'gps_timestamp', None)
123
+ gpsDatetime = None
124
+ gd = tryGet(img, 'gps_datestamp', None)
125
+ gt = tryGet(img, 'gps_timestamp', None)
89
126
 
90
- if gd and gt:
91
- offset = int(time.localtime().tm_gmtoff / 3600)
92
- gpsDatetime = gpsDt2Dt(gd, gt, offset=offset)
127
+ if gd and gt:
128
+ offset = int(time.localtime().tm_gmtoff / 3600)
129
+ gpsDatetime = gpsDt2Dt(gd, gt, offset=offset)
93
130
 
94
- ts = -1 if createDt is None else int(createDt.timestamp())
95
- mTs = -1 if modifyDt is None else int(modifyDt.timestamp())
96
- gpsTs = -1 if gpsDatetime is None else int(gpsDatetime.timestamp())
131
+ ts = -1 if createDt is None else int(createDt.timestamp())
132
+ mTs = -1 if modifyDt is None else int(modifyDt.timestamp())
133
+ gpsTs = -1 if gpsDatetime is None else int(gpsDatetime.timestamp())
134
+
135
+ if ts > 0:
97
136
  offset = max(mTs, gpsTs) - ts
98
- offsetDelta = datetime.fromtimestamp(offset, UTC) - EPOCH
99
-
100
- return {
101
- "width": width,
102
- "height": height,
103
- "latitude": latitude,
104
- "longitude": longitude,
105
- "datetime.create": dt2str(createDt),
106
- "datetime.modify": dt2str(modifyDt),
107
- "datetime.gps": dt2str(gpsDatetime),
108
- "ts": ts,
109
- "offset": offset,
110
- "offset.delta": str(offsetDelta),
111
- }
137
+ offsetDelta = str(datetime.fromtimestamp(offset, UTC) - EPOCH)
138
+ else:
139
+ offset = None
140
+ offsetDelta = None
141
+
142
+ return {
143
+ "width": width,
144
+ "height": height,
145
+ "latitude": latitude,
146
+ "longitude": longitude,
147
+ "datetime.create": dt2str(createDt),
148
+ "datetime.modify": dt2str(modifyDt),
149
+ "datetime.gps": dt2str(gpsDatetime),
150
+ "ts": ts,
151
+ "offset": offset,
152
+ "offset.delta": offsetDelta,
153
+ }
112
154
 
113
155
 
114
156
  class InvalidImageDataError(ValueError):
@@ -174,7 +216,7 @@ def getExif(segments):
174
216
  if seg[0:2] == b"\xff\xe1" and seg[4:10] == b"Exif\x00\x00":
175
217
  return seg
176
218
 
177
- return None
219
+ return b""
178
220
 
179
221
 
180
222
  def mergeSegments(segments, exif=b""):
@@ -210,49 +252,29 @@ def mergeSegments(segments, exif=b""):
210
252
  return b"".join(segments)
211
253
 
212
254
 
213
- def removeExif(src, new_file=None):
214
- output_is_file = False
215
- if src[0:2] == b"\xff\xd8":
216
- src_data = src
217
- file_type = "jpeg"
218
- else:
219
- with open(src, 'rb') as f:
220
- src_data = f.read()
221
- output_is_file = True
222
- if src_data[0:2] == b"\xff\xd8":
223
- file_type = "jpeg"
224
-
225
- if file_type == "jpeg":
226
- segments = genSegments(src_data)
227
- segments = list(filter(lambda seg: not (seg[0:2] == b"\xff\xe1"
228
- and seg[4:10] == b"Exif\x00\x00"),
229
- segments))
230
-
231
- segments = setComment(segments, "nt25.et")
232
- new_data = b"".join(segments)
233
-
234
- if isinstance(new_file, io.BytesIO):
235
- new_file.write(new_data)
236
- new_file.seek(0)
237
- elif new_file:
238
- with open(new_file, "wb+") as f:
239
- f.write(new_data)
240
- elif output_is_file:
241
- with open(src, "wb+") as f:
242
- f.write(new_data)
243
- else:
244
- raise ValueError("Give a second argument to 'remove' to output file")
255
+ def removeExif(src, optimize=False):
256
+ if optimize:
257
+ optimizeFile(src)
245
258
 
259
+ with open(src, 'rb') as f:
260
+ src_data = f.read()
246
261
 
247
- def transplant(exif_src, image, new_file=None):
248
- """
249
- py:function:: piexif.transplant(filename1, filename2)
262
+ segments = genSegments(src_data)
263
+ segments = list(filter(lambda seg: not (seg[0:2] == b"\xff\xe1"
264
+ and seg[4:10] == b"Exif\x00\x00"),
265
+ segments))
250
266
 
251
- Transplant exif from filename1 to filename2.
267
+ segments = setComment(segments, "nt25.et")
268
+ new_data = b"".join(segments)
269
+
270
+ with open(src, "wb+") as f:
271
+ f.write(new_data)
272
+
273
+
274
+ def transplant(exif_src, image, optimize=False):
275
+ if optimize:
276
+ optimizeFile(image)
252
277
 
253
- :param str filename1: JPEG
254
- :param str filename2: JPEG
255
- """
256
278
  if exif_src[0:2] == b"\xff\xd8":
257
279
  src_data = exif_src
258
280
  else:
@@ -262,41 +284,29 @@ def transplant(exif_src, image, new_file=None):
262
284
  segments = genSegments(src_data)
263
285
  exif = getExif(segments)
264
286
 
265
- if exif is None:
266
- raise ValueError("not found exif in input")
267
-
268
- output_file = False
269
- if image[0:2] == b"\xff\xd8":
270
- image_data = image
271
- else:
272
- with open(image, 'rb') as f:
273
- image_data = f.read()
274
- output_file = True
287
+ with open(image, 'rb') as f:
288
+ image_data = f.read()
275
289
 
276
290
  segments = genSegments(image_data)
277
291
  segments = setComment(segments, "nt25.et")
278
292
  new_data = mergeSegments(segments, exif)
279
293
 
280
- if isinstance(new_file, io.BytesIO):
281
- new_file.write(new_data)
282
- new_file.seek(0)
283
- elif new_file:
284
- with open(new_file, "wb+") as f:
285
- f.write(new_data)
286
- elif output_file:
287
- with open(image, "wb+") as f:
288
- f.write(new_data)
289
- else:
290
- raise ValueError("Give a 3rd argument to 'transplant' to output file")
294
+ with open(image, "wb+") as f:
295
+ f.write(new_data)
291
296
 
292
297
 
293
298
  def main():
294
299
  parser = argparse.ArgumentParser(description="EXIF tool")
295
- parser.add_argument('-v', '--version',
296
- help='echo version', action='store_true')
297
- parser.add_argument('-d', '--dump', help='dump meta', action='store_true')
298
- parser.add_argument('-r', '--rm', help='remove meta', action='store_true')
299
- parser.add_argument('-c', '--copy', type=str, help='copy meta')
300
+ parser.add_argument('-v', '--version', action='store_true',
301
+ help='echo version')
302
+ parser.add_argument('-o', '--optimize', action='store_true',
303
+ help='optimize jpg file, work with -r, -d, -c, -f')
304
+ parser.add_argument('-r', '--rm', action='store_true',
305
+ help='remove meta, use: -r -f FILE')
306
+ parser.add_argument('-d', '--dump', action='store_true',
307
+ help='dump meta, use: -d -f FILE')
308
+ parser.add_argument('-c', '--copy', type=str,
309
+ help='copy meta, use: -c SRC -f DST')
300
310
  parser.add_argument('-f', '--file', type=str, help='image file')
301
311
 
302
312
  args = parser.parse_args()
@@ -306,17 +316,20 @@ def main():
306
316
  return
307
317
 
308
318
  if args.file is None:
309
- print("usage: et [-h] [-v] [-r FILE] [-c FILE] [-d] [-f FILE]")
319
+ print("usage: et [-h] [-v] [-o] [-f FILE] [-d -f FILE]\n"
320
+ "\t\t[-r -f FILE] [-c SRC -f DST]")
310
321
  return
311
322
 
323
+ opt = True if args.optimize else False
324
+
312
325
  if args.dump:
313
- r = dumpExif(args.file)
326
+ r = dumpExif(args.file, optimize=opt)
314
327
  elif args.rm:
315
- r = removeExif(args.file)
328
+ r = removeExif(args.file, optimize=opt)
316
329
  elif args.copy:
317
- r = transplant(args.copy, args.file)
330
+ r = transplant(args.copy, args.file, optimize=opt)
318
331
  else:
319
- r = parseExif(args.file)
332
+ r = parseExif(args.file, optimize=opt)
320
333
 
321
334
  if r is not None:
322
335
  print(json.dumps(r, indent=2, sort_keys=False))
@@ -0,0 +1,105 @@
1
+ from nt25 import fio
2
+
3
+ import json
4
+ import requests
5
+ import argparse
6
+
7
+
8
+ kEncoding = 'gbk'
9
+ kWGSName = 'WGS-84'
10
+
11
+ kMaxTranCount = 50
12
+
13
+ kWGSCode = 4326
14
+ k50NCode = 32650
15
+ kCGS2000Z20Code = 4498
16
+ kCGS2000Z21Code = 4499
17
+
18
+ kURLFormatter = 'https://api.maptiler.com/coordinates/transform/' + \
19
+ '{cs}.json?key={key}&s_srs={s}&t_srs={t}'
20
+
21
+
22
+ def genCs4Tran(coordinates):
23
+ cs = []
24
+
25
+ for i in range(min(len(coordinates), kMaxTranCount)):
26
+ c = coordinates[i]
27
+ cs.append('{0},{1}'.format(c[0], c[1]))
28
+
29
+ return ';'.join(cs)
30
+
31
+
32
+ def fetchResult(url):
33
+ result = {}
34
+ res = requests.get(url)
35
+
36
+ try:
37
+ result = json.loads(res.text)
38
+ except Exception as e:
39
+ print(e, '\n\t', res.text)
40
+
41
+ return result
42
+
43
+
44
+ def transform(key, coordinates, startCode, toCode):
45
+ results = []
46
+
47
+ cc = [coordinates[i:i + kMaxTranCount]
48
+ for i in range(0, len(coordinates), kMaxTranCount)]
49
+
50
+ for c in cc:
51
+ url = kURLFormatter.format(cs=genCs4Tran(c),
52
+ key=key, s=startCode, t=toCode)
53
+ result = fetchResult(url)
54
+
55
+ if result and result['results'] and result['results'][0]['x'] is not None:
56
+ results.extend(result['results'])
57
+
58
+ for i in range(len(results)):
59
+ results[i]['z'] = coordinates[i][2] if len(coordinates[i]) > 2 else 0
60
+
61
+ return results
62
+
63
+
64
+ def main():
65
+ parse = argparse.ArgumentParser(description='Map Transform tool, '
66
+ 'from https://docs.maptiler.com/cloud'
67
+ '/api/coordinates/#transform-coordinates')
68
+
69
+ # default=kKey, required=False)
70
+ parse.add_argument('-k', '--key', type=str,
71
+ help='MapTiler key', required=True)
72
+ parse.add_argument('-i', '--input', type=str, required=True,
73
+ help='input file')
74
+ parse.add_argument('-o', '--output', type=str, help='output file',
75
+ default='out.csv')
76
+ parse.add_argument('-s', '--start', type=str, default='4498',
77
+ help='transform start code')
78
+ parse.add_argument('-t', '--to', type=str, default='4236',
79
+ help='transform code to')
80
+
81
+ args = parse.parse_args()
82
+
83
+ key = args.key
84
+ input = args.input
85
+ output = args.output
86
+ start = args.start
87
+ to = args.to
88
+
89
+ csv = fio.getCSV(input, width=3, startLine=1)
90
+
91
+ if csv is not None:
92
+ coordinates = []
93
+ for i in range(len(csv[0])):
94
+ coordinates.append((csv[0][i], csv[1][i], csv[2][i]))
95
+
96
+ result = transform(key, coordinates, start, to)
97
+
98
+ if len(result) > 0:
99
+ content = list(map(lambda r: (r['x'], r['y'], r['z']), result))
100
+ fio.saveCSV(content, output, colsInline=False)
101
+ print(f'Saved {len(result)} rows in {output}')
102
+
103
+
104
+ if __name__ == '__main__':
105
+ main()
@@ -0,0 +1,201 @@
1
+ import re
2
+ import os
3
+ import glob
4
+ import argparse
5
+ import time
6
+
7
+ from threading import Thread, Lock
8
+ from multiprocessing import cpu_count
9
+
10
+ from PIL import Image
11
+ import numpy as np
12
+ import matplotlib.colors as mcolors
13
+
14
+ gTLock = Lock()
15
+ gForceQuit = False
16
+
17
+ gMaxValue = 0
18
+ gShadowValue = 0
19
+
20
+ gCrop = None
21
+ gArea = None
22
+ gLast = None
23
+ gColors = None
24
+
25
+ kTifLevel = 255
26
+
27
+
28
+ def nsort(s):
29
+ sub = re.split(r'(\d+)', s)
30
+ sub = [int(c) if c.isdigit() else c for c in sub]
31
+ return sub
32
+
33
+
34
+ def genGradColors(gradStart, gradStop):
35
+ # ks = kTifLevel * kTifLevel
36
+ # yc = np.linspace(mcolors.to_rgba(gradStart),
37
+ # mcolors.to_rgba(gradStop), ks) * ks
38
+ # colors = [yc[ks - (i - kTifLevel) * (i - kTifLevel)]
39
+ # for i in range(0, kTifLevel)]
40
+
41
+ colors = np.linspace(mcolors.to_rgba(gradStart),
42
+ mcolors.to_rgba(gradStop), kTifLevel) * kTifLevel
43
+ return np.vstack([[0, 0, 0, 0], colors]).astype(np.uint8)
44
+
45
+
46
+ def splitArray(array, times):
47
+ chunk = len(array) // times
48
+ left = len(array) % times
49
+
50
+ result = []
51
+ for i in range(times):
52
+ start = i * chunk + min(i, left)
53
+ end = start + chunk + (1 if i < left else 0)
54
+ result.append(array[start:end])
55
+
56
+ return result
57
+
58
+
59
+ def dump(array):
60
+ shp = array.shape
61
+ for i in range(shp[0]):
62
+ print(array[i])
63
+
64
+
65
+ def ttp(array, path, maxValue):
66
+ global gTLock, gForceQuit, gCrop, gArea, gColors, gMaxValue, gLast
67
+
68
+ if gCrop is None or gArea is None or gColors is None:
69
+ return
70
+
71
+ for tif in array:
72
+ name = os.path.splitext(os.path.basename(tif))[0]
73
+ out = os.path.join(path, name + '.png')
74
+
75
+ image = Image.open(tif)
76
+ ta = np.array(image)
77
+ ta = ta[gCrop[0]:gCrop[1], gCrop[2]:gCrop[3]]
78
+
79
+ ta[ta < 0] = 0
80
+
81
+ with gTLock:
82
+ if ta.max() > gMaxValue:
83
+ gMaxValue = ta.max()
84
+
85
+ if gShadowValue > 0:
86
+ leap = maxValue / 40
87
+ ta = np.where(np.logical_and(gArea > 0, ta < gShadowValue),
88
+ gShadowValue, ta)
89
+ ta = np.where(gLast > ta + leap, gLast - leap, ta)
90
+ gLast = np.array(ta)
91
+ gArea = np.where(ta > gShadowValue, 1, gArea)
92
+
93
+ ta[ta > maxValue] = maxValue
94
+ ta = ta / maxValue * kTifLevel
95
+ Image.fromarray(gColors[ta.astype(np.uint8)], mode='RGBA').save(out)
96
+
97
+ if gForceQuit:
98
+ break
99
+ else:
100
+ print('.', end='', flush=True)
101
+
102
+
103
+ def main():
104
+ global gForceQuit, gCrop, gArea, gColors, gMaxValue, gLast
105
+
106
+ threadCounts = int(cpu_count() / 2)
107
+
108
+ parser = argparse.ArgumentParser(description="TIFF -> PNG")
109
+ parser.add_argument('-d', '--dir', help='Directory to handle',
110
+ default='.', type=str, required=False,)
111
+ parser.add_argument('-s', '--gradStart', help='Gradient color to Start',
112
+ default='#00FFFF', type=str, required=False,)
113
+ parser.add_argument('-t', '--gradStop', help='Gradient color to Stop',
114
+ default='#00008B', type=str, required=False,)
115
+ parser.add_argument('--TB', help='Crop TIFF from Top to Bottom', nargs='+',
116
+ # default=[1700, 3000], type=int, required=False,)
117
+ # default=[1350, 4250], type=int, required=False,)
118
+ default=[1400, 3550], type=int, required=False,)
119
+ # default=[2890, 2895], type=int, required=False,)
120
+ parser.add_argument('--LR', help='Crop TIFF from Left to Right', nargs='+',
121
+ default=[750, 2300], type=int, required=False,)
122
+ # default=[1650, 1655], type=int, required=False,)
123
+ parser.add_argument('-m', '--maxValue', help='Map Max Value',
124
+ default=16, type=int, required=False,)
125
+ parser.add_argument('-v', '--shadowValue', help='keep Value in Shadow',
126
+ default=1, type=int, required=False,)
127
+ parser.add_argument('-c', '--threadCounts', help='Minimum number of Threads',
128
+ default=threadCounts, type=int, required=False,)
129
+
130
+ args = parser.parse_args()
131
+ folder = args.dir
132
+ gradStart = args.gradStart
133
+ gradStop = args.gradStop
134
+
135
+ gColors = genGradColors(gradStart, gradStop)
136
+
137
+ TB = args.TB
138
+ LR = args.LR
139
+ gCrop = [TB[0], TB[1], LR[0], LR[1]]
140
+ gArea = np.zeros([TB[1] - TB[0], LR[1] - LR[0]], dtype=np.uint8)
141
+ gLast = np.zeros([TB[1] - TB[0], LR[1] - LR[0]])
142
+
143
+ maxValue = args.maxValue
144
+ threadCounts = args.threadCounts
145
+
146
+ gShadowValue = args.shadowValue
147
+
148
+ if gShadowValue > 0:
149
+ print(f'Shadow Value: {gShadowValue}')
150
+ threadCounts = 1
151
+
152
+ if threadCounts > 1:
153
+ print(f'Multi-threads to work: {threadCounts}T')
154
+
155
+ files = glob.glob(os.path.join(folder, '*.tif'))
156
+ files = sorted(files, key=nsort)
157
+
158
+ if len(files) == 0:
159
+ print("no TIF found")
160
+ return
161
+
162
+ path = os.path.join(folder, 'out')
163
+ if not os.path.exists(path):
164
+ os.makedirs(path)
165
+
166
+ tfs = splitArray(files, threadCounts)
167
+
168
+ td = []
169
+ for tif in tfs:
170
+ if tif is not None and len(tif) > 0:
171
+ t = Thread(target=ttp, args=(tif, path, maxValue,))
172
+ t.daemon = True
173
+ t.start()
174
+ td.append(t)
175
+
176
+ while True:
177
+ working = False
178
+ for t in td:
179
+ if t.is_alive():
180
+ working = True
181
+ break
182
+
183
+ if working:
184
+ try:
185
+ time.sleep(0.5)
186
+ except KeyboardInterrupt:
187
+ if not gForceQuit:
188
+ print("\n> WARN: Trying to terminate threads...")
189
+
190
+ gForceQuit = True
191
+ else:
192
+ break
193
+
194
+ if gForceQuit:
195
+ print("> WARN: Force quit!")
196
+ else:
197
+ print(f"\nTotal Max value: {maxValue}/{gMaxValue:.2f}")
198
+
199
+
200
+ if __name__ == '__main__':
201
+ main()
@@ -0,0 +1,3 @@
1
+ X,Y,H
2
+ 20510210.743588,4058386.735392,155.55
3
+ 20510196.183790,4057723.195846,123.45
Binary file
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes