Anonymous | Login | Signup for a new account | 21-11-24 09:43 UTC |
All Projects | SAS.Планета | Домен, сайт, форум, багтрекер | Доработка карты (ZMP) | Переводы и локализации | Прочее |
My View | View Issues | Change Log | Roadmap | Search |
View Issue Details [ Jump to Notes ] | [ Issue History ] [ Print ] | ||||||||||||
ID | Project | Category | View Status | Date Submitted | Last Update | ||||||||
0001203 | SAS.Планета | [All Projects] Хотелка | public | 06-03-2012 15:15 | 20-04-2020 21:11 | ||||||||
Reporter | Parasite | ||||||||||||
Assigned To | |||||||||||||
Priority | normal | Severity | tweak | Reproducibility | N/A | ||||||||
Status | confirmed | Resolution | open | ||||||||||
Platform | Windows | OS | Server | OS Version | 2003 | ||||||||
Product Version | 110418 | ||||||||||||
Target Version | 30xxxx.Vip | Fixed in Version | |||||||||||
Summary | 0001203: Сделать lossless склейку в JPG (с использованием libjpeg) | ||||||||||||
Description | Было бы неплохо(с), если бы САС при сведении в ЖПЕГ когда-нибудь научился юзать широко известную в узких кругах библиотеку libjpeg http://en.wikipedia.org/wiki/Libjpeg одной из фич которой является _беспотерьная_ трансформация JPG-изображений (в данном случае - тайлов) при операциях на глобальном полотне. Эту фичу юзает, например, общзеизвестная тулза JPEGtran (по ссылке выше), позволяющая делать сколь угодно многочисленные операции над JPG-контентом без ре-энкодинга на каждой стадии (lossless). | ||||||||||||
Additional Information | PS: юзаю JpegTran долгое время при сведении из, например, Zoomify. Тулза прекрастно вызывается из кастомных скриптов даже под виндой, http://jpegclub.org/jpegtran/ НИКАКОЙ деградации\артефактов на результирующем изображении по сравнении с исходным - не было замечено даже при _многотысячном_ вызове jpegtran при вклеивании каждого отдельного тайла (поодиночке, в циклах по Х\Y) в общее полотно. | ||||||||||||
Tags | склейка, скрипты | ||||||||||||
Attached Files | jpegtran.rar [^] (47,135 bytes) 13-03-2012 13:30 jtest.zip [^] (216,519 bytes) 23-04-2012 06:16 | ||||||||||||
Notes | |
(0005866) zed (manager) 06-03-2012 15:19 edited on: 06-03-2012 15:25 |
САС и так использует libjpeg между прочим. |
(0005867) Parasite (administrator) 06-03-2012 15:40 edited on: 06-03-2012 15:41 |
Уже? В составе последней имеющейся у меня сборки вижу ijl15.dll, но не вижу ничего похожего на libjpg. Сейчас скачаю последнюю ночнушку, гляну. Если токи да - то пардон. |
(0005880) Tolik (manager) 07-03-2012 04:34 edited on: 07-03-2012 04:39 |
Инфа 100% (если память не изменяет, это Zed сделал, поэтому перевёл в resolved (zed)) P.S. Кстати, ijl15.dll в релизе и бете нет. |
(0005882) zed (manager) 07-03-2012 05:03 |
Использовать-то использует, но не lossless (если вообще возможна безпотерьная склейка жпегов). |
(0005887) Tolik (manager) 07-03-2012 05:23 |
Почему ж невозможна? Для такого красивого размерчика 256х256 всё должно отлично клеиться. Рановато закрыл? Сделаете lossless слейку? |
(0005888) zed (manager) 07-03-2012 05:29 |
Ну, если Parasite покажет свои "скрипты" при помощи которых он клеит жпеги (lossless вариант), то можно подумать. Тем более, что сорцы jpegtran'а есть и "ход мыслей" можно попробовать повторить в САСе. |
(0005901) Parasite (administrator) 07-03-2012 08:39 |
>P.S. Кстати, ijl15.dll в релизе и бете нет. Ключевая фраза выше "в составе последней _имеющейся у меня_". Про текущие бетки - см.про нераспаковываемость 7z без необходимости обновлять пакер (и полсистемы зависимостей к нему). >если Parasite покажет свои "скрипты" при помощи которых он клеит жпеги (lossless вариант) А скрипт-то причем? Скрипт у меня просто вызывает сторонний жпегтран в фоне столько раз, сколько нужно. Раз - значит раз. Стотыщ-значит стотыщ, благо что работает оно весьма быстро, да и форкается на ура. Нужны были ключики вызова жпегтрана, или как? |
(0005905) zed (manager) 07-03-2012 10:25 |
Нужен алгоритм, чтобы мы пришли от состояния "у нас на винте кучка jpeg" до "у нас на винте lossless снимок". Скажем, на примере склейки 4-х тайлов на 2-ом зуме. И чем подробнее, тем лучше. |
(0005909) Parasite (administrator) 07-03-2012 11:08 |
Да не вопрос. Вот на примере последней lossless-работы (http://sasgis.org/forum/viewtopic.php?f=21&t=1905&start=0) на Питоне. Качает тайлы (многопоточно, с очередью и контролем ерроров, по зумифай-алго), сводит используя пару темповых жпегтранов через свитч друг друга, результат - один жпег максимального уровня. По результату на ссылке выше - отработало примерно за сутки. Ради лосслесса лично я вполне готов потерпеть. ----------------------------- import sys import time, os import re import urllib.request, urllib.parse import optparse import tempfile, shutil import subprocess import queue import threading from math import ceil, floor def main(): (opts, args) = parser.parse_args() if (opts.url is None): parser.print_help() exit(-1) if (opts.out is None): print("ERR: The output file '-o' must be given\n") parser.print_help() exit(-1) Dezoomify(opts) def urlConcat(url1, url2): if url1[-1] == '/': url1 = url1[0:-1] if url2[0] == '/': url2 = url2[1:] return url1 + '/' + url2 def getFilePath(level, col, row, ext): name = str(level) + '-' + str(col) + '-' + str(row) + '.' + ext return name def getUrl(url): req_headers = { 'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.A.B.C Safari/525.13', 'Referer': 'http://google.com' } request = urllib.request.Request(url, headers=req_headers) opener = urllib.request.build_opener() response = opener.open(request) code = response.code headers = response.headers contents = response.read().decode(errors='ignore') return code, headers, contents class Dezoomify(): def getImage(self, imageDir, outputDestination): def newDownloadThread(): t = threading.Thread(target=downloader) t.daemon = True t.start() def downloader(): if downloadQueue.empty(): return url, col, row = downloadQueue.get() destination = tileName(col, row) if self.debug: print("\tINF: Downloading tile: " + destination) urllib.request.urlretrieve(url, destination) joinQueue.put((col, row)) if not downloadQueue.empty(): newDownloadThread() def tileName(col, row): return os.path.join(self.tileDir, str(col) + '_' + str(row) + '.' + self.ext) downloadQueue = queue.Queue() joinQueue = queue.Queue() for col in range(self.xTiles): for row in range(self.yTiles): if self.nodownload: joinQueue.put((col, row)) continue tileIndex = self.getTileIndex(self.zoomLevel, col, row) tileGroup = tileIndex // self.tileSize if self.debug: print(("\tINF: Adding image number (row, col) to queue: " + str(row).rjust(2) + ', ' + str(col).rjust(2) + ': Index: '+ str(tileIndex).rjust(3) + ', Tilegroup: %d'% tileGroup)) filepath = getFilePath(self.zoomLevel, col, row, self.ext) url = imageDir + '/' + 'TileGroup%d' % tileGroup + '/' + filepath downloadQueue.put((url, col, row)) for i in range(self.nthreads): newDownloadThread() totalTiles = self.xTiles * self.yTiles numJoined = 0 tmpimgs = [None, None] for i in range(2): fhandle = tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) tmpimgs[i] = fhandle.name fhandle.close() if self.debug: print( '\tINF: Created temporary image file: ' + tmpimgs[i] ) activeTmp = 0 while numJoined < totalTiles: while joinQueue.empty(): if threading.active_count() == 1 and joinQueue.empty(): print("ERR: Tile downloading stopped prematurely!") os.unlink(tmpimgs[0]) os.unlink(tmpimgs[1]) return time.sleep(0.05) col, row = joinQueue.get() if numJoined == 0: cmd = [self.jpegtran, '-copy', 'all', '-crop', '%dx%d+0+0' % (self.width, self.height), '-outfile', tmpimgs[activeTmp], tileName(col, row)] subprocess.call(cmd) if self.debug: print( '\tINF: Adding tile (row ' + str(row) +', col ' + str(col) + ') to the image' ) cmd = [self.jpegtran, '-copy', 'all', '-drop', '+%d+%d' % (col*self.tileSize, row*self.tileSize), tileName(col, row), '-outfile', tmpimgs[(activeTmp+1)%2], tmpimgs[activeTmp]] subprocess.call(cmd) activeTmp = (activeTmp+1) % 2 numJoined += 1 cmd = [self.jpegtran, '-copy', 'all', '-optimize', '-outfile', outputDestination, tmpimgs[activeTmp]] subprocess.call(cmd) os.unlink(tmpimgs[0]) os.unlink(tmpimgs[1]) def getImageDirectory(self, url): try: content = urllib.request.urlopen(url).read().decode(errors='ignore') except Exception: print(("ERR: Specified directory not found. Check the URL.\nException: %s " % sys.exc_info()[1])) sys.exit() imagePath = None m = re.search('zoomifyImagePath=([^\'"&]*)[\'"&]', content) if m: imagePath = m.group(1) if not imagePath: m = re.search('ZoomifyCache/[^\'"&.]+\\.\\d+x\\d+', content) if m: imagePath = m.group(0) if not imagePath: print("ERR: Source directory not found. Ensure the given URL contains a Zoomify object.") sys.exit() else: print(('INF: Found zoomifyImagePath: %s' % imagePath)) netloc = urllib.parse.urlparse(imagePath).netloc if not netloc: parsedURL = urllib.parse.urlparse(url) pathParts = parsedURL.path.split('/') m = re.search('\.', pathParts[-1]) if m: del(pathParts[-1]) path = '/'.join(pathParts) url = urllib.parse.urlunparse([ parsedURL.scheme, parsedURL.netloc, path, None, None, None]) imageDir = urlConcat(url, imagePath) else: imageDir = imagePath if self.debug: print(('INF: Found image directory: ' + imageDir)) return imageDir def getMaxZoom(self): width = int(ceil(self.maxWidth/float(self.tileSize))) height = int(ceil(self.maxHeight/float(self.tileSize))) self.levels = [] while True: self.levels.append((width, height)) if width == 1 and height == 1: break else: width = int(ceil(width/2.0)) height = int(ceil(height/2.0)) self.levels.reverse() def getProperties(self, imageDir, zoomLevel): xmlUrl = imageDir + '/ImageProperties.xml' code, headers, content = getUrl(xmlUrl) print (xmlUrl) m = re.search('WIDTH="(\d+)"', content) if m: self.maxWidth = int(m.group(1)) else: print('ERR: Width not found in ImageProperties.xml') sys.exit() m = re.search('HEIGHT="(\d+)"', content) if m: self.maxHeight = int(m.group(1)) else: print('ERR: Height not found in ImageProperties.xml') sys.exit() m = re.search('TILESIZE="(\d+)"', content) if m: self.tileSize = int(m.group(1)) else: print('ERR: Tile size not found in ImageProperties.xml') sys.exit() self.getMaxZoom() self.maxZoom = len(self.levels) if not zoomLevel: self.zoomLevel = self.maxZoom-1 else: zoomLevel = int(zoomLevel) if zoomLevel < self.maxZoom and zoomLevel >= 0: self.zoomLevel = zoomLevel else: self.zoomLevel = self.maxZoom-1 if self.debug: print(('ERR: the requested zoom level is not available, defaulting to maximum (%d)' % self.zoomLevel )) self.width = self.maxWidth / 2**(self.maxZoom - self.zoomLevel - 1) self.height = self.maxHeight / 2**(self.maxZoom - self.zoomLevel - 1) self.maxxTiles = self.levels[-1][0] self.maxyTiles = self.levels[-1][1] self.xTiles = self.levels[self.zoomLevel][0] self.yTiles = self.levels[self.zoomLevel][1] if self.debug: print(( '\tMax zoom level: %d (working zoom level: %d)' % (self.maxZoom-1, self.zoomLevel) )) print(( '\tWidth (overall): %d (at given zoom level: %d)' % (self.maxWidth, self.width) )) print(( '\tHeight (overall): %d (at given zoom level: %d)' % (self.maxHeight, self.height ))) print(( '\tTile size: %d' % self.tileSize )) print(( '\tWidth (in tiles): %d (at given level: %d)' % (self.maxxTiles, self.xTiles) )) print(( '\tHeight (in tiles): %d (at given level: %d)' % (self.maxyTiles, self.yTiles) )) print(( '\tTotal tiles: %d (to be retreived: %d)' % (self.maxxTiles * self.maxyTiles, self.xTiles * self.yTiles))) def getTileIndex(self, level, x, y): index = x + y * int(ceil( floor(self.width/pow(2, self.maxZoom - level - 1)) / self.tileSize ) ) for i in range(1, level+1): index += int(ceil( floor(self.width /pow(2, self.maxZoom - i)) / self.tileSize ) ) * \ int(ceil( floor(self.height/pow(2, self.maxZoom - i)) / self.tileSize ) ) return index def getUrls(self, opts): if not opts.list: self.imageDirs = [ opts.url ] self.outNames = [ self.out ] else: listFile = open( opts.url, 'r') self.imageDirs = [] self.outNames = [] i = 1 for line in listFile: line = line.strip().split(' ', 1) if len(line) == 1: root, ext = os.path.splitext(self.out) self.outNames.append(root + '%03d' % i + ext) i += 1 elif len(line) == 2: m = re.search('\\.' + self.ext + '$', line[1]) if not m: line[1] += '.' + self.ext self.outNames.append(os.path.join(os.path.dirname(self.out), line[1])) else: continue self.imageDirs.append( line[0] ) def setupDirectory(self, destination): if self.store: root, ext = os.path.splitext(destination) if not os.path.exists(root): if self.debug: print(( 'INF: Creating image storage directory: %s' % root)) os.makedirs(root) self.tileDir = root else: self.tileDir = tempfile.mkdtemp(prefix='dezoomify_') if self.debug: print(( 'INF: Created temporary image storage directory: %s' % self.tileDir)) def __init__(self, opts): self.debug = opts.debug self.tempo = opts.tempo self.ext = 'jpg' self.store = opts.store self.out = opts.out self.jpegtran = opts.jpegtran self.nodownload = opts.nodownload self.nthreads = int(opts.nthreads) if self.nodownload: self.store = True if self.tempo: tempfile.tempdir = opts.tempo self.getUrls(opts) i = 0 for imageDir in self.imageDirs: destination = self.outNames[i] if not opts.base: imageDir = self.getImageDirectory(imageDir) self.getProperties(imageDir, opts.zoomLevel) self.setupDirectory(destination) self.getImage(imageDir, destination) if self.debug: print( 'INF: Dezoomifed image created and saved to ' + destination ) if not self.store: shutil.rmtree(self.tileDir) if self.debug: print( 'INF: Erased the temporary directory and its contents' ) i += 1 if __name__ == "__main__": try: main() finally: None ============================== Если коротко: 1. качаем все запланированные тайлы в кучу (отдельными потоками, не мешающими основному). 2. Создаем ЖПЕГ полотно размера равного результирующему изображению (в виде файла). 3. Создаем его копию. 4. Последовательно клеим (юзая жпегтран) каждый тайл в полотно, а результат пихая в копию полотна. По завершении жпегтрана - полотно и копию меняем местами. Повторять пока тайлы не кончатся. 5. Результат (один, самый последний жпег) сохраняем в указанное юзером место. Смываем все темпы. Всё. PS: работает с любым зумифаем. Надеюсь. :) |
(0005912) zed (manager) 07-03-2012 11:20 |
Зачем копия полотна и почему мы их должны менять местами, после вклеивания каждого тайла? |
(0005917) Parasite (administrator) 07-03-2012 12:30 |
>Зачем копия полотна и почему мы их должны менять местами, после вклеивания каждого тайла? Потому что standalone-JPEGtran работает как <source> <destination>. Читаем исходное полотно с первого, пишем после вклейки во второе - при следующем вызове <destination> первого должен стать <source> последующего. |
(0005953) zed (manager) 08-03-2012 11:06 |
>при следующем вызове <destination> первого должен стать <source> последующего Да, но имена источника и приёмника могут совпадать. Проверь, на всякий случай? В скрипте вместо: cmd = [self.jpegtran, '-copy', 'all', '-drop', '+%d+%d' % (col*self.tileSize, row*self.tileSize), tileName(col, row), '-outfile', tmpimgs[(activeTmp+1)%2], tmpimgs[activeTmp]] сделать: cmd = [self.jpegtran, '-copy', 'all', '-drop', '+%d+%d' % (col*self.tileSize, row*self.tileSize), tileName(col, row), '-outfile', tmpimgs[activeTmp], tmpimgs[activeTmp]] и закомментить/удалить строчку: activeTmp = (activeTmp+1) % 2 |
(0005954) Parasite (administrator) 08-03-2012 11:39 |
>и закомментить/удалить строчку: А на кой это всё вообще? Мы препарируем мой скрипт, или обучаем САС беспотерьке? Если второе - то функционал надо вводить в САС, а не звать сторонний жпегтран в данном случае. Ведь звать его придется ровно столько раз, сколько у нас тайлов на сведение - и распаковывать\запаковывать всё полотно в памяти, особенно если source с destination совпадать будут (придется еще и ждать снятия системных локов с постоянно перезаписываемого файла). В моем случае в зумифае число тайлов весьма небольшое как правило по сравнению с картами, и я зову жпегтран и ворочаю всё полотно исключительно в уме, до кучи и читая\записывая темпы на рам-диск - с зумифаем это пока что прокатывает, памяти пока что хватает, и можно открыть хоть сотню темпов, если в сотню потоков сводить... В случае же САСа - и звать упаримся, и поворочать на среднестатистическом железе тоже не всегда сумеем. Мы же планируем чтобы работало у всех секретулек тоже, а не только у меня в памяти, так? Этот функционал имхо надо пихать в сводилку САСа, подсмотрев фичу в сорцах жпегтрана (например). Имхо. |
(0006039) Parasite (administrator) 13-03-2012 13:31 |
>Проверь, на всякий случай? Приложил в шапку скомпиленный под винду жпегтран. |
(0006418) zed (manager) 11-04-2012 16:57 edited on: 11-04-2012 17:47 |
Облом. Функции crop и drop работают с жпегами в памяти, соответственно и клеить снимки больших разрешений не выйдет: >c:\jtest>jpegtran -crop 32000x32000+0+0 tile.jpg crop.jpg >Insufficient memory (case 4) Для 32-х битной системы максимальное разрешение снимка будет где-то в районе 24k*24k pix (естественно, при достаточном размере памяти. Если будет нехватка памяти, то и того меньше). Заставить их работать по другому, не представляется возможным. Ввиду вышесказанного фича ещё интересна? |
(0006441) Parasite (administrator) 20-04-2012 09:46 edited on: 20-04-2012 11:02 |
>Insufficient memory (case 4) >Для 32-х битной системы Ну и нормально. 24Кх24К жпега с лослессом все же лучше чем 24Кх24К без оного - и будет достаточно для 95% задач (все равно ТАКОЙ жпег потом открывать будет проблематично, даже если и создастся), а кому надо действительно БОЛЬШИЕ losless полотна - будет подождать решения 295-го тикета вместе со мной. :) |
(0006442) vdemidov (manager) 20-04-2012 10:09 |
Кстати. Сейчас 295-й тикет сможет реализовать почти любой чайник где-то за пару часов. Там самое сложно нарисовать формочку выбора параметров склейки. |
(0006443) zed (manager) 20-04-2012 14:16 |
Хм, хоть это и склейка по сути, но фактически получается что мы экспортируем jpeg тайлы в файл, на подобии экспорта в zip и проч. Поэтому даже не знаю как это лучше сделать в сасе. Выходит, что нужно делать класс-наследник от TThreadExportAbstract, а не от TThreadMapCombineBase. И соответственно, на страничке Экспорт, появится пункт JPEG (Lossless). При этом будут недоступны опции создания привязок и разбиения на части. Полная чехорда выходит. |
(0006444) Parasite (administrator) 20-04-2012 15:34 |
>Сейчас 295-й тикет сможет реализовать почти любой чайник Подождем, пока он туда придет и реализует. :) >Хм, хоть это и склейка по сути, но фактически получается что мы экспортируем jpeg тайлы в файл, на подобии экспорта в zip и проч. Предлагаю на время отладки просто опцию в инишнике типа "JPEGlossless=true", при наличии которой - выводить ЛЮБОЙ сводимый сасом жпег через жпегтран. При отсутствии, соответственно - как обычно. Потом, если все срастется и всем понравится - вывод через жпегтран можно оставить единственным. Я думаю - никто же против более качественных жпегов не будет? :) |
(0006445) zed (manager) 20-04-2012 19:10 |
Так-с, с безпотерьной склейкой разобрался. Кода оказалось с гулькин нос, правда обнаружилось, что libjpeg-turbo не в полной мере поддерживает losslless, так что пришлось заюзать официальный libjpeg. В итоге, добавятся 2 dll-ки: jpeg8.dll (libjpeg, 190k) и transupp.dll (API для losslless трансформаций, 20k). Осталось набраться мужества и прикрутить это всё к сасу. >выводить ЛЮБОЙ сводимый сасом жпег через жпегтран Ага, разогнался. Без потерь можно сводить жпеги с очень большими оговорками: - тайлы в кэше должны быть в jpeg формате - наложение слоёв/меток невозможно - цветокоррекция недоступна - операция ресурсоёмкая и отжирает много ОЗУ Так что - только опционально, по галочке. И по дефолту галочка будет снята. |
(0006446) Parasite (administrator) 21-04-2012 06:23 |
>- тайлы в кэше должны быть в jpeg формате Ну разумеется. Lossless такой lossless, что если изначально не жпег - без репака не обойтись. >- наложение слоёв/меток невозможно >- цветокоррекция недоступна Кстати, да. Слона-то я и не приметил...в САСе оно вроде есть, но я ни разу не пользовался - потому и не обращал внимания на эти пункты. >- операция ресурсоёмкая и отжирает много ОЗУ Угу. В основном из-за ворочанья глобального полотна в памяти * число вклеек тайлов. >Так что - только опционально, по галочке. И по дефолту галочка будет снята. Как скажешь, начальнег. Не вопрос. |
(0006459) Parasite (administrator) 23-04-2012 05:02 |
Меня, собственно, беспокоит вопрос быстродействия этого всего на относительно больших склеиваемых листах. Так как [стандартные] алгоритмы ворочают все полотно в памяти, а при выходе - пишут на диск (в жпег разумеется), то время всей склейки будет (постоянное подчитывание глобального растра+растягивание в памяти+вклейка+запись на диск) * число_тайлов_для_склейки. При использовании стандартных алгоритмов и стандартного же жпегтрана (того что в шапке) - весь процесс весьма и весьма печален даже при наличии 32Гб памяти и рамдиска. Например вот это изображение http://sasgis.org/forum/viewtopic.php?f=21&t=1905 из тайлов вышеуказанный скрипт+жпегтран сводили около суток непрерывной работы (правда включая и выкачку тайлов с интернетов, ну да там всего 105Мб...). Если САС будет точно так же сводить сутками - то смысл фичи теряется к сожалению, с тем же успехом оно все сводится и сторонними скриптами еще вчера. То есть, придется таки переработать сам жпегтран хотя бы для того, чтобы он постоянно не читал\писал весь растр при обработке каждого тайла - а держал в памяти пока вся задача не будет выполнена и все тайлы не вклеятся... Zed, ты уже проводил тесты быстродействия в связке с САСом? |
(0006460) zed (manager) 23-04-2012 06:15 edited on: 23-04-2012 06:15 |
Чисто синтетически: время склейки 22k*22k = ~ 7 часов (~ 3 сек. на тайл). Убийственно медленно, по сравнению с обычной склейкой. Ща приаттачу тестовую утиль, сможешь оценить на своей системе. |
(0006461) zed (manager) 23-04-2012 06:17 edited on: 23-04-2012 06:19 |
В аттаче, кстати, и сорцы есть, на случай если кто решит поучаствовать. |
(0006465) Parasite (administrator) 23-04-2012 10:21 |
>время склейки 22k*22k = ~ 7 часов (~ 3 сек. на тайл) Охх, щщи....... :((( Не думал, что всё будет настолько печально.... Есть ли возможность изменить логику работы всего алгоритма - чтобы он не переписывал все полотно после к.тайла, а держал бы в памяти до окончания вклейки всех тайлов - и только тогда уже сливал в файл? Имхо это единственное, что глобально повлияет на быстродействие. Все же жпегтран сам по себе - коммандлайновая утиль и ей сам Аллах велел работать читать\отрабатывать\записывать\отваливаться, а у нас-то тут практически "потоковая вклейка от обеда и до пока_кэш_не_кончится"...:) Если возможности поднять быстродействие не найдется - имхо, хотелка становится бесполезной и ее можно будет закрыть. Ведь за то же время оно отрабатывается и простым батником с кучей натравлений штатного жпегтрана на имеющийся кэш, без потерь человеко\часов на САСовый кодинг... >Ща приаттачу тестовую утиль Вечерком поюзаю. Отпишусь. |
(0006466) zed (manager) 23-04-2012 12:04 |
>Есть ли возможность изменить логику работы всего алгоритма Есть пара мыслей. Попробую. |
(0007474) Parasite (administrator) 19-06-2012 03:14 |
>>Есть ли возможность изменить логику работы всего алгоритма >Есть пара мыслей. Попробую. Есть ли новости? |
(0007478) zed (manager) 19-06-2012 04:49 |
Нет. Видимо быстрее не получится. |
(0007479) Parasite (administrator) 19-06-2012 05:05 |
>Нет. Видимо быстрее не получится. Та не к спеху, собссно. Не тороплю. Просто roadmap дай, хоть приблизительно? Месяц - знач месяц, три - знач три... |
(0007480) vdemidov (manager) 19-06-2012 05:09 |
Попробуй клеить сначала полоски с размерами кратными 8 пикселам в отдельный файл. А потом уже собирать эти полоски в большой файл. То есть, склеил полоску - добавил в результирующий файл и тд. Должно быть гораздо быстрее за счет отсутствия рендомных записей на винчестер. |
(0007482) zed (manager) 19-06-2012 05:23 |
>roadmap дай В том виде, как оно есть сейчас, сделать склейку можно за день максимум. Но не оптимизированный вариант, я так понимаю, нафиг кому нужен. >Должно быть гораздо быстрее за счет отсутствия рендомных записей на винчестер Там не из-за винта тормоза, а из-за необходимости распаковки/запаковки жпега (служебных данных) для записи/чтени каждого тайла. К слову, если загрузить жпег целиком в память (исключив винт, как возможное узкое место) и делать из него безпотерьный crop, то всё происходит так же медленно и печально. |
(0007489) vdemidov (manager) 19-06-2012 05:54 |
Ну если в памяти все так же медленно и печально, то можно закрывать. >22k*22k = ~ 7 часов (~ 3 сек. на тайл). Это явный перебор. |
(0007491) zed (manager) 19-06-2012 06:00 |
Перебор, но с другой стороны, фича полезная и найдёт своих маньяков. А в TODO записать: Оптимизировать losslless, и год поставить - 2020 :) |
(0007497) vdemidov (manager) 19-06-2012 06:08 |
Ну, как хотите :) Для меня фитча бесполезная. Я склейку использую, только если мне нужно кусок карты распечатать и отдать знакомым. |
(0007499) Parasite (administrator) 19-06-2012 06:30 |
А что если просто вклеивать body от каждого тайла напрямик в результирующее большое полотно, и потом к нему генерить хидер? То есть, вообще ничего никуда не перепаковывать, а обойтись сугубо файловыми операциями? Насколько я помню, боди от жпега вполне себе самостоятельно и может быть распаковано даже со сторонним хидером (более того - некоторые карторесурсы отдают вообще только боди, а хидер там одинаков для всех и приклеивается "на ходу" уже на стороне клиента). Грубо говоря, взять два смежных jpeg-тайла, оторвать у обоих хидеры, два боди вклеить в результирующий файл, и приписать ему зановосгенеренный хидер уже согласно новой картинки. По идее - должно открыться как родное, и будет вполне себе lossless.... |
(0007515) Dima2000 (developer) 19-06-2012 07:48 |
Я хотел такой способ предложить ещё с самого начала, не перепаковывать тайлы и вообще не использовать библиотеки работы с jpeg, а юзать чисто пересылки бинарных данных. Но посмотрев внимательнее на формат jpeg-а, образумился: очень похоже что какие-то таблицы, жизненно важные для распаковки, сидят в хидере и в общем случае могут быть разными для разных файлов. Конкретно, кажется таблица квантования такова. Когда jpeg файлы создаём сами, то всегда можем выбрать одинаковую таблицу для всех файлов, но вот если файлы получены из сторонних источников, это может и не соблюдаться. А тогда лишь перепаковка. Хотя, никто не мешает один раз выбрать таблицу для всего большого jpeg-а и потом перепаковывать тайлы по ней, дозаписывая в результирующий файл уже готовое body (но перепакованое!). От перепаковки не уйти, lossless не будет, но хотя бы памяти не надо будет очень много и винту легче. А, немного не прав, если выходной формат lossless, то будет вполне себе безпотерьно, лишь от распаковки тайлов не уйти. |
(0007522) Parasite (administrator) 19-06-2012 09:42 |
>Конкретно, кажется таблица квантования такова. Когда jpeg файлы создаём сами, то всегда можем выбрать одинаковую таблицу для всех файлов, но вот если файлы получены из сторонних источников, это может и не соблюдаться. А тогда лишь перепаковка. Угу. Но, как правило, картосервисы в пределах себя любимых таким не балуются (да и на тайлы обычно нарезается автоматом - с одинаковыми настройками к каждому тайлу) - и все тайлы отличаются только боди, а остальное стандартно. Как правило - но возможно есть и исключения. Теоретически можно в процессе склейки проверять каждый тайл на это дело, и по нахождении расхождений - либо еррорить, либо переквантовать этот злобный тайл к общей картинке и далее вклеивать как обычно. Либо же перепахивать сорцы жпегтрана, отучая его перепаковывать все полотно после к.тайла - а делать это только раз, в финале... Либо же вообще забить на этот тикет и сосредоточиться на заведомо более вкусном lossless\limitless сведении в RAW, предоставив хомякам потом делать с ним что угодно но уже своими силами. Но там свои тараканы (например куча дисковых операций + место на винте будет весьма сильно уходить, со жпегом ни в какие ворота не сравнится). |
(0013342) Globbus (reporter) 27-11-2013 14:36 |
Тема заглохла? Уж больно печально, когда 9 кБ тайл z19 в склейке раздувается в 30 кБайт при худшем визуальном качестве (даже на 100%). Думаю, даже многочасовая обработка не являлась бы помехой - можно запускать на ночь. |
(0013344) zed (manager) 28-11-2013 06:00 |
Ну, вы как бы первый, кто проявил заинтересованность в фиче, не смотря на её недостатки. |
(0013414) Globbus (reporter) 12-12-2013 09:48 |
Думаю ситуация здесь, как и в ряде других случаев в жизни - "а парни-то и не знают..." Не знают о багтрекере, не знают lossless-операциях с jpeg. |
Users who viewed this issue | |
User List | Anonymous (5722x), mrjack (2x), Parasite (1x), ingener (1x), k-dmitriy (3x) |
Total Views | 5729 |
Last View | 21-11-2024 09:43 |
Issue History | |||
Date Modified | Username | Field | Change |
06-03-2012 15:15 | Parasite | New Issue | |
06-03-2012 15:18 | Parasite | Additional Information Updated | View Revisions |
06-03-2012 15:19 | zed | Note Added: 0005866 | |
06-03-2012 15:22 | zed | Note Edited: 0005866 | View Revisions |
06-03-2012 15:25 | zed | Note Edited: 0005866 | View Revisions |
06-03-2012 15:29 | vdemidov | Status | new => resolved |
06-03-2012 15:29 | vdemidov | Resolution | open => no change required |
06-03-2012 15:29 | vdemidov | Assigned To | => vdemidov |
06-03-2012 15:29 | vdemidov | Status | resolved => closed |
06-03-2012 15:40 | Parasite | Note Added: 0005867 | |
06-03-2012 15:40 | Parasite | Status | closed => feedback |
06-03-2012 15:40 | Parasite | Resolution | no change required => reopened |
06-03-2012 15:41 | Parasite | Note Edited: 0005867 | View Revisions |
07-03-2012 04:34 | Tolik | Note Added: 0005880 | |
07-03-2012 04:35 | Tolik | Status | feedback => resolved |
07-03-2012 04:35 | Tolik | Fixed in Version | => 120808 |
07-03-2012 04:35 | Tolik | Resolution | reopened => fixed |
07-03-2012 04:35 | Tolik | Assigned To | vdemidov => zed |
07-03-2012 04:36 | Tolik | Note Edited: 0005880 | View Revisions |
07-03-2012 04:38 | Tolik | Product Version | .Nightly => 110418 |
07-03-2012 04:38 | Tolik | Target Version | => 120808 |
07-03-2012 04:39 | Tolik | Note Edited: 0005880 | View Revisions |
07-03-2012 05:03 | zed | Note Added: 0005882 | |
07-03-2012 05:23 | Tolik | Note Added: 0005887 | |
07-03-2012 05:29 | zed | Note Added: 0005888 | |
07-03-2012 07:10 | Tolik | Assigned To | zed => |
07-03-2012 07:10 | Tolik | Status | resolved => acknowledged |
07-03-2012 07:10 | Tolik | Resolution | fixed => open |
07-03-2012 07:10 | Tolik | Fixed in Version | 120808 => |
07-03-2012 07:10 | Tolik | Target Version | 120808 => |
07-03-2012 07:10 | Tolik | Summary | Юзание libjpeg при сведении в JPG => Сделать lossless склейку в JPG (с использованием libjpeg) |
07-03-2012 07:17 | vdemidov | Status | acknowledged => confirmed |
07-03-2012 07:18 | vdemidov | Target Version | => 50xxxx |
07-03-2012 08:39 | Parasite | Note Added: 0005901 | |
07-03-2012 10:25 | zed | Note Added: 0005905 | |
07-03-2012 11:08 | Parasite | Note Added: 0005909 | |
07-03-2012 11:20 | zed | Note Added: 0005912 | |
07-03-2012 12:30 | Parasite | Note Added: 0005917 | |
08-03-2012 11:06 | zed | Note Added: 0005953 | |
08-03-2012 11:39 | Parasite | Note Added: 0005954 | |
13-03-2012 13:30 | Parasite | File Added: jpegtran.rar | |
13-03-2012 13:31 | Parasite | Note Added: 0006039 | |
22-03-2012 08:27 | gpsMax | Tag Attached: склейка | |
22-03-2012 08:28 | gpsMax | Tag Attached: скрипты | |
11-04-2012 16:57 | zed | Note Added: 0006418 | |
11-04-2012 17:47 | zed | Note Edited: 0006418 | View Revisions |
20-04-2012 09:46 | Parasite | Note Added: 0006441 | |
20-04-2012 10:09 | vdemidov | Note Added: 0006442 | |
20-04-2012 11:02 | Parasite | Note Edited: 0006441 | View Revisions |
20-04-2012 14:16 | zed | Note Added: 0006443 | |
20-04-2012 15:34 | Parasite | Note Added: 0006444 | |
20-04-2012 19:10 | zed | Note Added: 0006445 | |
21-04-2012 06:23 | Parasite | Note Added: 0006446 | |
23-04-2012 05:02 | Parasite | Note Added: 0006459 | |
23-04-2012 06:15 | zed | Note Added: 0006460 | |
23-04-2012 06:15 | zed | Note Edited: 0006460 | View Revisions |
23-04-2012 06:16 | zed | File Added: jtest.zip | |
23-04-2012 06:17 | zed | Note Added: 0006461 | |
23-04-2012 06:19 | zed | Note Edited: 0006461 | View Revisions |
23-04-2012 10:21 | Parasite | Note Added: 0006465 | |
23-04-2012 12:04 | zed | Note Added: 0006466 | |
19-06-2012 03:14 | Parasite | Note Added: 0007474 | |
19-06-2012 04:49 | zed | Note Added: 0007478 | |
19-06-2012 05:05 | Parasite | Note Added: 0007479 | |
19-06-2012 05:09 | vdemidov | Note Added: 0007480 | |
19-06-2012 05:23 | zed | Note Added: 0007482 | |
19-06-2012 05:54 | vdemidov | Note Added: 0007489 | |
19-06-2012 06:00 | zed | Note Added: 0007491 | |
19-06-2012 06:08 | vdemidov | Note Added: 0007497 | |
19-06-2012 06:30 | Parasite | Note Added: 0007499 | |
19-06-2012 07:48 | Dima2000 | Note Added: 0007515 | |
19-06-2012 09:42 | Parasite | Note Added: 0007522 | |
27-11-2013 14:36 | Globbus | Note Added: 0013342 | |
28-11-2013 06:00 | zed | Note Added: 0013344 | |
12-12-2013 09:48 | Globbus | Note Added: 0013414 | |
13-10-2015 08:20 | vdemidov | Target Version | 50xxxx => 30xxxx.Vip |
My View | View Issues | Change Log | Roadmap | Search |
Copyright © 2007 - 2024 SAS.Planet Team |