SDL_GetRGB - неверно определяет цвет?
| |
Morfay | Дата: Понедельник, 09 Июля 2012, 13:22 | Сообщение # 1 |
почетный гость
Сейчас нет на сайте
| Добрый день. Недавно, для одной идеи потребовалось определить мне цвета пикселей одной картинки. Все это делается с помощью SDL. Выкопал функцию SDL_GetRGB(Uint32 pixel, SDL_PixelFormat *fmt, Uint8 *r, Uint8 *g, Uint8 *b) - получает цвет пикселя и записывает его в указатели (r, g, b). Она как бы и подходит для задуманного, но первые тесты выявили странный эффект - берем залитую одним фоном картинку, определяем первый пиксель - 136, 0, 21, - все правильно, цвет именно такой. Берем второй пиксель = 140, 0, 21, следующий = 144, 0, 21 (видно каккое-то смещение на 4). Сначала грешил на то, что неправильно определяю пиксель(а цвет правильно показывает), но если картинка состоит из одного фона, то какой пиксель не возьми, везде же будет одинаковый цвет?!
как "хожу" по пикселям Code int bpp = img->format->BytesPerPixel; Uint32 pdst = *((Uint32*)img->pixels) + bpp*posX +img->pitch*posY ; //переход на необходимый пиксель
Вопросов несколько: 1) Вполне возможно "хожу" по пикселям я неправильно. Потому если кто поправит, то будет хорошо. 2) Пользовал ли кто функцию SDL_GetRGB(...)? И не было подобных проблем? 3) На случай, если решения с SDL_GetRGB не будет - как еще можно определить цвет пикселя?
Заранее спасибо.
|
|
| |
Нохчи | Дата: Понедельник, 09 Июля 2012, 14:54 | Сообщение # 2 |
заслуженный участник
Сейчас нет на сайте
| Quote (Morfay) ((Uint32*)img->pixels) ((Uint32*)ptr)+1 к примеру сместит указатель не на один байт, а на sizeof(Uint32). Попробуй заменить на любой тип длиною в 1 байт.
Многие вопросы по Windows отпадут, если посмотреть тут
|
|
| |
Morfay | Дата: Понедельник, 09 Июля 2012, 15:14 | Сообщение # 3 |
почетный гость
Сейчас нет на сайте
| Попробовал. Цвет 137, 0, 21. Странно, почему-то прибавляется к красному смещение по пикселям (По мануалу - пиксель занимает от 2 до 4 байт - потому и первоначальное смещение было на 4). Но почему так, не пойму.
|
|
| |
Нохчи | Дата: Понедельник, 09 Июля 2012, 15:43 | Сообщение # 4 |
заслуженный участник
Сейчас нет на сайте
| Специально скачал этот сдл и полез читать документацию. Хз как ты там попробовал, но у меня все работает отлично. Code int main(int argc, char *argv[]){ SDL_Surface *img = SDL_LoadBMP("test.bmp"); int bpp = img->format->BytesPerPixel; for(int x = 0; x<img->w; x++) for(int y = 0; y<img->h; y++) { Uint32 pixel = *(Uint32*)(((Uint8*)img->pixels)+bpp*x +img->pitch*y); Uint8 r,g,b; SDL_GetRGB(pixel, img->format, &r, &g, &b); printf("%d,%d,%d\n",r,g,b); }
return 0; } вот мой test.bmp
Многие вопросы по Windows отпадут, если посмотреть тут
|
|
| |
Morfay | Дата: Понедельник, 09 Июля 2012, 16:19 | Сообщение # 5 |
почетный гость
Сейчас нет на сайте
| Хм, код такой же. Единственное отличие - у меня картинка формата png. Но раз работает нормально, то значит затуп у меня... и я его только что нашел
было
Code Uint32 pdst = *((Uint32*)img->pixels) + bpp ;
А надо так
Code Uint32 pdst = *((Uint32*)img->pixels + bpp) ;
Черт, мне стыдно - такая глупая ошибка. Но теперь все работает. Спасибо.
|
|
| |
Нохчи | Дата: Понедельник, 09 Июля 2012, 16:29 | Сообщение # 6 |
заслуженный участник
Сейчас нет на сайте
| Quote (Morfay) Uint32 pdst = *((Uint32*)img->pixels + bpp) ; То есть ты примерно так написал? Code for(int x = 0; x<img->w; x++) for(int y = 0; y<img->h; y++) { Uint32 pixel = *((Uint32*)img->pixels+bpp*x +img->pitch*y); Uint8 r,g,b; SDL_GetRGB(pixel, img->format, &r, &g, &b); printf("%d,%d,%d\n",r,g,b); } И оно не крэшится? Наверное это магия ( ;
Многие вопросы по Windows отпадут, если посмотреть тут
Сообщение отредактировал Нохчи - Понедельник, 09 Июля 2012, 16:30 |
|
| |
Morfay | Дата: Понедельник, 09 Июля 2012, 16:35 | Сообщение # 7 |
почетный гость
Сейчас нет на сайте
| Наверное. Как я понял, img->pixel возвращает адрес на первый пиксель картинки. bpp в данном случае размер пикселя. То бишь, получить адрес первого, прибавить к нему смещение по строкам и столбцам (представив рисунок как массив пикселей) - получим адрес нужного. Потом получаем значение (*(...) вроде как разименовывание). Так что вроде как все логично...
Дело в том, что я самоучка. Потому только за, если ты объяснишь мне что тебя смущает.
|
|
| |
Нохчи | Дата: Понедельник, 09 Июля 2012, 16:45 | Сообщение # 8 |
заслуженный участник
Сейчас нет на сайте
| *((Uint32*)img->pixels) - первый пиксель *((Uint32*)img->pixels + bpp) - четвертый пиксель *((Uint32*)img->pixels + bpp*2) - восьмой пиксель и так далее. а если так *(Uint32*)((Uint8*)img->pixels) - первый пиксель *(Uint32*)((Uint8*)img->pixels+bpp) - второй пиксель *(Uint32*)((Uint8*)img->pixels+bpp*2) - третий пиксель и т.д.
Многие вопросы по Windows отпадут, если посмотреть тут
|
|
| |
Archido | Дата: Понедельник, 09 Июля 2012, 16:54 | Сообщение # 9 |
Сэнсэй
Сейчас нет на сайте
| Нохчи в общем-то на примере показал, что там с арифметикой указателя у тебя беда. Когда ты делаешь *((Uint32*)img->pixels + 1), то оно как бы неявно превращается в *((Uint32*)img->pixels + 1 * sizeof(Uint32)). Т.е. грубо говоря смещение в байтах зависит от типа указателя (размера его типа данных), к которому ты в итоге приводишь. А когда нагромаждается куча приведений типов, то они выполняются в порядке справа налево.
Quote (Morfay) Дело в том, что я самоучка Ну, в программировании по-другому и не бывает
C++ - он особенный. С помощью него можно не только выстрелить себе в ногу, но и повеситься в пустой комнате:)
Сообщение отредактировал Archido - Понедельник, 09 Июля 2012, 16:56 |
|
| |
Morfay | Дата: Понедельник, 09 Июля 2012, 16:55 | Сообщение # 10 |
почетный гость
Сейчас нет на сайте
| А, понял. bpp идет как Uint8. В моем случае img->pixel доводим до Uint32, а потом складываем с Uint8. Оно-то сложится, но не так как надо. Потому преобразуем все в один тип, сложить, потом преобразовать результат в другой. Так?
|
|
| |
Нохчи | Дата: Понедельник, 09 Июля 2012, 16:57 | Сообщение # 11 |
заслуженный участник
Сейчас нет на сайте
| Quote (Morfay) А, понял. bpp идет как Uint8 bpp идет как int. Во втором и девятом сообщениях все написано.
Многие вопросы по Windows отпадут, если посмотреть тут
Сообщение отредактировал Нохчи - Понедельник, 09 Июля 2012, 16:58 |
|
| |
Morfay | Дата: Понедельник, 09 Июля 2012, 17:20 | Сообщение # 12 |
почетный гость
Сейчас нет на сайте
| Мне еще многому надо научиться. Спасибо больше, получил больше чем хотел... В общем, не хочу новую тему открывать, решил написать зачем это мне.
Quote Ну, в программировании по-другому и не бывает Как и многие другие, увлекаюсь играми. Еще в колледже делал простенькие штуки (типа змеек), пробовал замахиваться даже на файтинг. Сейчас, для того, чтобы выучить язык, пытаюсь делать свой маленький двухмерный движок. В связи с этим, появилось несколько моих тем, с различными вопросами (Нохчи уже не раз в них отвечал, за что ему большое спасибо). Сейчас ковыряюсь в физической части движка. Хочу попробовать реализовать автоматическое определение столкновения повышенной точности между сложными объектами. Суть в том, что строится полигоны по точкам у двух сталкивающихся объектах и идет проверка на пересечение отрезков. Точки для полигона определяются перебором пикселей. То есть, идя по перовму ряду, находим пиксель отличный от самого первого (фонового) - это уже начинается наш объект => запоминаем координаты. Идем по следующему ряду и т.д. и т.п. Тесты по работы с пикселями (код адаптировал с одного урока по SDL) показал - на зеркальное отображение картинки весом в пол метра тратилось около 0,5 секунды. После некоторых улучшений, пробег по пикселям составлял до 0.01 - 0.02 секунды. Есть еще пару идей, чтобы уменьшить и это время. В теории все выглядит очень соблазнительно (+ первые тесты неплохи), сейчас проверяю на практике.
Написал как-то сумбурно, надеюсь поймете :D. Выслушаю любые комментарии и идеи.
|
|
| |
Нохчи | Дата: Понедельник, 09 Июля 2012, 17:33 | Сообщение # 13 |
заслуженный участник
Сейчас нет на сайте
| Идея вобщем не очень понятна, но полигоны по двум точкам не строятся, нужно три как минимум. А вобще загугли per-pixel collision detection
Многие вопросы по Windows отпадут, если посмотреть тут
Сообщение отредактировал Нохчи - Понедельник, 09 Июля 2012, 17:33 |
|
| |
Archido | Дата: Понедельник, 09 Июля 2012, 18:03 | Сообщение # 14 |
Сэнсэй
Сейчас нет на сайте
| Morfay Не, ну раз все и так неплохо работает, то и идеи уже подкидывать смысла не имеет . Скажу только, что попиксельный collision все равно будет относительо медлителен при наличии большого кол - ва объектов, и часто он бывает избыточен. Простые объекты легко описываются прямоугольниками, а сложные можно описать группой прямоугольников - будет такой составной физический объект. При реальном применении такой точности вполне достаточно, а работать оно будет довольно шустро.
Ну, а если все таки хочется большой точности, то можно кобминировать эти способы. Идея то вообщем-то проста: изображения разбивается на N-ное (небольшое) количество ячеек(клеток) и пересечения сначала проверяются между ними (т.е. ячейки одного изображения с ячейками другого). После того как определили взаимно пересекающиеся ячейки, то в этих ячейках уже ведется попиксельная проверка. Таким образом часть лишних пикселей можно отсеять и выиграть в быстродействии. + далее можно продолжать совершенствовать сие дело и вместо простой сетки использовать QuadTree , ну полет мыслей не ограничен
C++ - он особенный. С помощью него можно не только выстрелить себе в ногу, но и повеситься в пустой комнате:)
|
|
| |
Morfay | Дата: Понедельник, 09 Июля 2012, 19:00 | Сообщение # 15 |
почетный гость
Сейчас нет на сайте
| Идеи помогают сделать лучше (была у меня проблемка с отрисовкой всех объектов, так идея решающая ее не дотягивала до того, чтобы ее реализовать. Посоветовался с более опытными людьми, указали от чего избавиться, а на что обратить внимание.)
Quote Скажу только, что попиксельный collision все равно будет относительо медлителен при наличии большого кол - ва объектов, и часто он бывает избыточен
Ага, я тоже подумал, что если проверять попиксельно все объекты, то это будет жестоко. Потому решил скомбинировать с прямоугольной системой обнаружения - сначала проверяем все объекты им, если есть столкновение, то определяем уже точнее.
Я читал про способ что ты описал - он хорош (я его несколько раз видел). Но я как идеалист ( ) хочу попробовать сделать автоматическое распознавание. То есть, впихнул спрайт, а алгоритм сам найдет края и построит многоугольник, описывающий объект. Самая заметная проблема - время (понимаю, что расчет может быть не очень шустрым). Ну как реализую напишу что получилось, если интересно.
|
|
| |
Archido | Дата: Вторник, 10 Июля 2012, 10:19 | Сообщение # 16 |
Сэнсэй
Сейчас нет на сайте
| Morfay Пиши конечно, посмотрим, что выйдет и идей еще подкинем
C++ - он особенный. С помощью него можно не только выстрелить себе в ногу, но и повеситься в пустой комнате:)
|
|
| |
Morfay | Дата: Вторник, 10 Июля 2012, 11:08 | Сообщение # 17 |
почетный гость
Сейчас нет на сайте
| Опробовал способ. Для корректировки добавил параметр "точность" - переменная, от которой зависит как точно будет пробегать алгоритм по рядам (при 1 пробегает по всем. При 10 - по каждой 10 строке или(и) столбцу). После некоторых преобразований (нам не нужно описывать весь объект, достаточно стороны, с которой идет столкновение (определяется из первоначального прямоугольного столкновения)), с точностью 1 многоугольник (вернее ломанная) строится за 0,5 секунды - достаточно много (высота изображения - 243 пикселя, количество точек = 143). Если нужно быстрота, то не подходит, если нужна высокая точность (очень высокая, так как линия проведенная по точкам полностью "облегает" объект в мельчайших подробностях), то может и сойдет. Но в большинстве игр такая точность не нужна. Выставив точность = 10, получил результат за 0.03 сек, и 13 точек (Довольно точных, для игры будет в самый раз). Точность = 20 - 6 точек, выполнение за 0,015 сек. Но результат уже чуть хуже, иногда линии проходит через объект (хотя, думаю, даже для файтинга погрешность в 10 пикселей не критична). В общем, результаты неплохие, возможно придумаю еще пару способов, как уменьшить время выполнения без снижения точности.
Не понимаю. Подумал о том, что можно по ряду идти не по каждому пикселю, а проскакивать через несколько. Но, при проверке каждого второго, выиграл примерно 0,001 секунды (погрешность в 1 пиксель мелочь, но и выигрыш не очень ). При проскакивании через 10, выиграл... 0,002 секунды (причем погрешность ~9 пикселей уже не радует). Нда, дополнительные результаты не радуют.
Сообщение отредактировал Morfay - Вторник, 10 Июля 2012, 11:17 |
|
| |
Archido | Дата: Вторник, 10 Июля 2012, 12:59 | Сообщение # 18 |
Сэнсэй
Сейчас нет на сайте
| Ммм, ща еще раз все перечитал на свежую голову - т.е. ты получается строишь контур из отрезков на основе пикселей и проверяешь пересечения между, собсна, отрезками. Так? Если так, то можно строить контур заранее при загрузке изображения, например. Для анимированных спрайтов может это и жирновато, но я не думаю, что таких будет очень много. А вообще, хотелось бы увидеть как выглядит построенный многоугольник. Наверняка для отладки на это можно визуально посмотреть.
C++ - он особенный. С помощью него можно не только выстрелить себе в ногу, но и повеситься в пустой комнате:)
Сообщение отредактировал Archido - Вторник, 10 Июля 2012, 13:11 |
|
| |
Morfay | Дата: Вторник, 10 Июля 2012, 13:28 | Сообщение # 19 |
почетный гость
Сейчас нет на сайте
| Quote (Archido) Ммм, ща еще раз все перечитал на свежую голову - т.е. ты получается строишь контур из отрезков на основе пикселей и проверяешь пересечения между, собсна, отрезками. Так? Если так, то можно строить контур заранее при загрузке изображения, например. Для анимированных спрайтов может это и жирновато, но я не думаю, что таких будет очень много. А вообще, хотелось бы увидеть как выглядит построенный многоугольник. Наверняка для отладки на это можно визуально посмотреть.
Строить и хранить контуры в начале? Ну можно наверное, но может быть жирновато по времени. Дело в том, что еще при описании идеи я понимал - многоугольник, полностью описывающий фигуру, - это очень долго (По первым тестам, полностью описанная сложная фигура с шириной и высотой в 246 пикселей, с точностью 1, многоугольник строиться около секунды). Если представить, что в спрайте около 10-20 сильно различающихся фигур (это как минимум), то уже имеем 20 секунд. А фигур может быть больше. В итоге, тратить пару минут на загрузку даже простенькой бродилки - не хорошо. Потому начал думать, как снизить нагрузку: 1) Использовать вначале прямоугольное определение столкновений - оно быстрое, позволит определить сталкивающиеся объекты. 2) Незачем строить полностью многоугольник, достаточно определить стороны, которые сталкиваются. Это позволяет сделать определение прямоугольных столкновений. 3) В зависимости от направлений, прогоняем нужный кадр спрайта, рисуем ломанную линию, запоминая точки в массив. Адаптировать координаты под экран (сейчас координаты считываются со спрайта) Это то что реализовано сейчас.Дальше есть варианты: 4) Определяем линии и потом проверяем на пересечения. Или 4) Нафиг определять линии, сразу использовать точки (нужные формулы уже выведены и только использовать и оттестить).
Вот как показать тебе ломаную (кроме как скрином кадра в спрайте и координат точек), я не в курсе. Сам проверял, отрыв в пэинте спрайт и проведя линии по точкам (точность 10 брал - 13 точек просмотрел быстро).
Сообщение отредактировал Morfay - Вторник, 10 Июля 2012, 13:30 |
|
| |
Archido | Дата: Вторник, 10 Июля 2012, 13:49 | Сообщение # 20 |
Сэнсэй
Сейчас нет на сайте
| Quote (Morfay) Строить и хранить контуры в начале? Ну можно наверное, но может быть жирновато по времени. Дело в том, что еще при описании идеи я понимал - многоугольник, полностью описывающий фигуру, - это очень долго (По первым тестам, полностью описанная сложная фигура с шириной и высотой в 246 пикселей, с точностью 1, многоугольник строиться около секунды). Если представить, что в спрайте около 10-20 сильно различающихся фигур (это как минимум), то уже имеем 20 секунд. Хм, довольно таки долго и правда. Ну, можно при создании спрайта это делать оффлайн и просто загружать изображение + заранее построенные контуры, считай свой формат сочинять , но по идее нафиг оно - лучше сразу делать попиксельное, без всяких отрезков. И использовать оптимизацию, разбивая на ячейки, о чем я выше писал.
Quote (Morfay) Вот как показать тебе ломаную (кроме как скрином кадра в спрайте и координат точек), я не в курсе. Сам проверял, отрыв в пэинте спрайт и проведя линии по точкам (точность 10 брал - 13 точек просмотрел быстро). Можно и так. Просто для отладки удобно было бы выводить эти точки + построенные по ним отрезки, чтобы видить как оно построилось. Прямо во время отрисовки спрайта. Есть мысли, что этих отрезков слишком много получается. Если я, конечно, правильно понял алгоритм. Для этого как раз неплохо скрин посмотреть.
C++ - он особенный. С помощью него можно не только выстрелить себе в ногу, но и повеситься в пустой комнате:)
Сообщение отредактировал Archido - Вторник, 10 Июля 2012, 13:49 |
|
| |
|