![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Максимально быстро подготовить картинку для передачи по сети [D7, WinXP]
Citizen © (2005-03-24 01:09:00)Цель моей программы: передача скриншотов по сети. Сама передача по сети - это другой вопрос. На данный момент меня интересует как максимально быстро снять скрингот и подготовить его к отправке. Конечно за RAdmin'ом я не гонюсь, но хотелось бы сделать, чтобы передавался хотя бы 1 скрин в секунду. Возникли кое-какие проблемы.
Делаю следующее: снимаю скриншот, преобразую его в jpg, чтобы размер был поменьше, затем преобразую в поток для передачи по сети.
var jpg:TJPEGImage;
dd:hdc;
bmp:TBitmap;
ms:TMemoryStream;
begin
dd:=GetDC(0);
bmp:=TBitmap.Create;
bmp.Width:=Screen.Width;
bmp.Height:=Screen.Height;
StretchBlt(bmp.Canvas.Handle,0,0,Screen.Width,Screen.Height,dd,0,0,Screen.Width,Screen.Height,MERGECOPY);
ReleaseDC(0,dd);
jpg:=TJPEGImage.Create;
jpg.CompressionQuality:=70;
jpg.Compress;
jpg.Assign(bmp);
ms:= TMemoryStream.Create;
jpg.SaveToStream(ms);
bmp.free;
jpg.free;
ms.free;
end;
Ставлю этот код в обработчик таймера, и каждые 500 мс допустим уменя снимается скриншот. Но возникают тормоза каждые полсекунды. Оказалось из-за этой строчки:
jpg.SaveToStream(ms);
Если её закомментировать, то никаких тормозов нет. Это я картинку преобразую в поток для дальнейшей передачи по сети.
Это действие занимает где-то 250 мс. Причём, если первоначально скриншот не преобразовывать в jpg, а сразу из BMP в поток:
bmp.SaveToStream(ms);
то эта команда выполняется быстрее раза в полтора.
Так вот мне интересно, как обойти эти тормоза? Как можно ещё картинку в поток преобразовать, но чтобы не притормаживало?
И ещё вопрос: для уменьшения передаваемых данных хочу сравнивать новый скриншот с предыдущим и посылать только изменённую информацию. Как это можно организовать (сравнение и получение изменившихся частей)?
Anatoly Podgoretsky (2005-03-26 15:26:00) [1]Earser (25.03.05 13:42) [9]
Ты это сам придумал?
Смотри DiamondShark © (25.03.05 14:34) [12] точнее не скажешь, данный алгоритм никакими видеоданными не оперирует. Только байтами, слова и т.д.
Citizen (2005-03-26 15:04:00) [2]
> именнно при "преобразовании в поток" и происходит "сжатие
> в jpg", ибо jpg - это формат хранения данных, а не их представления.
> При jpg.Assign(bmp) скорей всего никакого преобразования
> нет, т.к. твой TJPEGImage тоже работает с Bitmap-ом, как
> и вся Windows
> чего жмет в только что созданном объекте ?
Да, действительно, изменил код на следующий:
jpg:=TJPEGImage.Create;
jpg.Assign(bmp);
jpg.CompressionQuality:=70;
jpg.Compress;
ms:= TMemoryStream.Create;
jpg.SaveToStream(ms);
Теперь задержка при сжатии jpg - 300 мс, а задержки при записи в поток нет. Но задержки при записи в поток нет, потому что у меня комп быстрый и получившаяся jpg мало весит. Попробовал на медленном компе - при записи в поток тоже есть задержка.
А сжатие в jpg вообще 1 секунду занимает!
Как ещё можно сжать bmp, но так чтобы быстро. RLE как то плохо сжимает, почти никакого эффекта.
Вот ещё что, строчка
bmp.SaveToStream(ms);
тоже притормаживает, окола 100 мс. Как то можно записать данные в поток другим образом без тормозов?
Где то слышал, что надо сначала выделить в потоке память для картинки, а потом туда картичнку уже записать, но как это сделать не знаю.
ЮЮ (2005-03-26 11:20:00) [3]>По моему легче преобразовать в jpg да и послать - быстрее будет.
>Так тормоза возникают не при сжатии, а при преобразовании в поток: jpg.SaveToStream(ms);
именнно при "преобразовании в поток" и происходит "сжатие в jpg", ибо jpg - это формат хранения данных, а не их представления. При jpg.Assign(bmp) скорей всего никакого преобразования нет, т.к. твой TJPEGImage тоже работает с Bitmap-ом, как и вся Windows
jpg:=TJPEGImage.Create;
jpg.CompressionQuality:=70;
jpg. Compress;
чего жмет в только что созданном объекте ?
Citizen (2005-03-26 08:16:00) [4]
> Выполнить попиксельную операцию XOR над картинками.
> Ненулевые пикселы будут соответствовать изменённым пикселам.
>
> Потом разбиваем изображение на прямоугольники по примерно
> такому алгоритму:
>
> 1. Ищем минимальный прямоугольник, включающий все ненулевые
> пикселы
> 2. Разбиваем его пополам поперёк длиннейшей стороны
> 3. Повторяем рекурсивно для каждого вновь полученного прямоугольника,
> пока либо не будет достигнуто некоторое количество фрагментов,
> либо некий минимальный размер фрагмента.
>
> Некоторые фрагменты могут оказаться вырожденными, их исключаем.
> Пакуем без потерь (например, RLE или ZIP) фрагменты и передаём.
Попробовал реализовать нечто подобное, только минимальный прямоугольник не искал, и рекурсию не делал. А разбил весь скриншот на прямоугольники, например 20 на 20, то есть на 400 прямоугольников. Потом смотрел, если в прямоугольнике есть хотя бы одна единица, то вырезаю его для отправки, а где все нули - те пропускаю.
В результате получается n прямоугольников - изменившиеся части картинки.
Но всё это по времени достаточно продолжительно получается - 150 мс.
Соответственно, чем мельче сетка разбития - тем дольше всё это будет происходить, но тем более точно определим изменения картинки и если изменений немного, то и данных для отсылки будет немного. Чем реже сетка, тем менее точно будут зафиксированы изменения - тем больше инфы посылать придётся.
По моему легче преобразовать в jpg да и послать - быстрее будет.
Магнитон Борыч (2005-03-25 20:46:00) [5]> Citizen © (25.03.05 19:41) [13]
> По-моему это будет очень долго выполняться, если попиксельно сравнивать.
Я бы не сказал
http://www.delphimaster.ru/articles/pixels/index.html
Citizen (2005-03-25 19:41:00) [6]
> Выполнить попиксельную операцию XOR над картинками.
> Ненулевые пикселы будут соответствовать изменённым пикселам.
По-моему это будет очень долго выполняться, если попиксельно сравнивать.
> Пакуем без потерь (например, RLE или ZIP) фрагменты и передаём.
Упаковка тоже много времени займёт.
DiamondShark (2005-03-25 14:34:00) [7]
> Eraser © (25.03.05 13:59) [11]
Разумеется, нет! Точно так же, как не поддерживает high color, альфа-канал и арабскую кодировку.
А всё по одной и той же причине: RLE -- это алгоритм (точнее, класс алгоритмов) сжатия битового (байтового, символьного и т.п.) потока, и ему по-барабану смысловое содержание сжимаемых данных.
Eraser (2005-03-25 13:59:00) [8]DiamondShark ©
Ещё скажи что поддерживает....
DiamondShark (2005-03-25 13:47:00) [9]
> RLE не поддерживает true color
Да чито ви гойворите?!
Earser (2005-03-25 13:42:00) [10]Citizen ©
RLE не поддерживает true color. Самый лучший выбор- zlib.
DiamondShark (2005-03-25 12:20:00) [11]
> Интересует меня 2-ой пункт, как сравнить две картинки, но
> так, чтобы была высокая скорость?
Выполнить попиксельную операцию XOR над картинками.
Ненулевые пикселы будут соответствовать изменённым пикселам.
Потом разбиваем изображение на прямоугольники по примерно такому алгоритму:
1. Ищем минимальный прямоугольник, включающий все ненулевые пикселы
2. Разбиваем его пополам поперёк длиннейшей стороны
3. Повторяем рекурсивно для каждого вновь полученного прямоугольника, пока либо не будет достигнуто некоторое количество фрагментов, либо некий минимальный размер фрагмента.
Некоторые фрагменты могут оказаться вырожденными, их исключаем.
Пакуем без потерь (например, RLE или ZIP) фрагменты и передаём.
Citizen (2005-03-25 01:17:00) [12]>например, жать граф.данные RLE-алгоритмом, он весьма быстр ..
Вот скачал пример сжатия данных с помощью RLE-алгоритма
http://torry.net/pages.php?id=357
Там самый первый пример.
Быстрый то он может и быстрый, даже не стал проверять. Но только степень сжатия очень маленькая. Попробовал сжать bmp-файл размером 3.6 мегабайта, стал 3 мегабайта. Лучше уж в jpg преобразовывать.
Анонимщик1 (2005-03-24 17:21:00) [13]Есть DirectShow фильтр PushSource - вполне подойдет для видеопотока.
Anatoly Podgoretsky (2005-03-24 14:37:00) [14]Citizen © (24.03.05 14:24) [4]
То есть вы предлагаете посылать скриншоты отдельных окон программ, а потом в самой программе их уже располагать как нужно?
Именно так и поступают серьезные програмы, только они еще дальше идут передают или измененые регионы или вообще классовые параметры, а не картинки.
Citizen (2005-03-24 14:24:00) [15]>jpg это сжатие, winrar тоже не мгновенно жмёт
Так тормоза возникают не при сжатии, а при преобразовании в поток:
jpg.SaveToStream(ms);
>идея - не скриншот передавать, а данные для создания картинки -> т.е. если рабочий стол, то какие обои, какие ярлыки, как >изменилось положение мыши, какие клавиши были нажаты, >программы запущены(программы, отдельные окна скриншотом - не >весь экран, а только их, как получается при нажатии alt + >print screen)
Мысль конечно интересная, только как это делать? Откуда брать такую информацию (об окнах, ярлыках, обоях)? А самое главное, как потом это в своём окне всё отображать. Ну допустим узнал я что были нажаты такие то клавиши и запущены такие то процессы, и что?
То есть вы предлагаете посылать скриншоты отдельных окон программ, а потом в самой программе их уже располагать как нужно?
>> то эта команда выполняется быстрее раза в полтора
>что ж тут удивительного ?
>разумеется, перекодировка битмапа в jpg-формат - алгоритм >нетривиальный ..
Я имел ввиду, что команда
bmp.SaveToStream(ms);
выполняется в полтора раза быстрее, чем
jpg.SaveToStream(ms);
JPG уже получен, а этими командами я сохраняю данные в поток.
>например, жать граф.данные RLE-алгоритмом, он весьма быстр ..
Где можно про это почитать? Есть ли какие-нибудь примерчики?
А как насчёт быстрого сохранения в поток?
>разбивай битмап на прямоугольные регионы
>сжимай и посылай (целиком) только те регионы, в которых >произошло изменение хотя бы, например, одного пикселя
Что-то мне кажется, что это займёт кучу времени - все эти манипуляции.
Всё ясно с этим:
1). Разбиваю новый скриншот.
2). Последовательно сравниваю каждую часть с предыдущей.
3). Сжимаю изменившиеся части и отсылаю.
Интересует меня 2-ой пункт, как сравнить две картинки, но так, чтобы была высокая скорость?
И есть ли какой-то метод, чтобы сравнить оба скриншота без разбивки? Получить только изменившиеся пиксели, их упаковать и отослать?
>Этот вопрос неоднократно обсуждался.
А можешь кинуть какие-нибудь ссылочки, где это обсуждалось, а то я искал, ничего конкретного не нашёл.
Иль посовету, где поискать.
>Лучшее, что мне запомнилось - рекомендация ставить хук на >запись в видеопамять или что-то вроде того (точно не помню, >тем более, что не понял как это). Это по существу вопроса.
Нда, я тоже не особо понимаю. Может кто объяснит из знающих?
>А что касается сравнения и сжатия разницы, то для этого >существуют кодеки видеопотоков, и ты вряд ли напишешь то же >самое оптимальнее. Так что смотри в эту сторону.
То, что я сам такое не напишу - это точно. Может тогда можно готовыми решениями воспользоваться? Есть ли что-нибудь подобное?
Анонимщик1 (2005-03-24 12:15:00) [16]Этот вопрос неоднократно обсуждался. Лучшее, что мне запомнилось - рекомендация ставить хук на запись в видеопамять или что-то вроде того (точно не помню, тем более, что не понял как это). Это по существу вопроса.
А что касается сравнения и сжатия разницы, то для этого существуют кодеки видеопотоков, и ты вряд ли напишешь то же самое оптимальнее. Так что смотри в эту сторону.
Digitman (2005-03-24 10:03:00) [17]
> то эта команда выполняется быстрее раза в полтора
что ж тут удивительного ?
разумеется, перекодировка битмапа в jpg-формат - алгоритм нетривиальный ..
> как обойти эти тормоза?
например, жать граф.данные RLE-алгоритмом, он весьма быстр ..
> хочу сравнивать новый скриншот с предыдущим и посылать только
> изменённую информацию
совершенно логично.
> Как это можно организовать (сравнение и получение изменившихся
> частей)?
разбивай битмап на прямоугольные регионы
сжимай и посылай (целиком) только те регионы, в которых произошло изменение хотя бы, например, одного пикселя
uny (2005-03-24 08:10:00) [18]jpg это сжатие, winrar тоже не мгновенно жмёт
идея - не скриншот передавать, а данные для создания картинки - т.е. если рабочий стол, то какие обои, какие ярлыки, как изменилось положение мыши, какие клавиши были нажаты, программы запущены(программы, отдельные окна скриншотом - не весь экран, а только их, как получается при нажатии alt + print screen)