Пятніца пытанні і адказы 2012-08-31: атрымання і інтэрпрэтацыі дадзеных выявы

2015-11-11 11:17:07 / author: sharkov views 665Total views: 665 / 5Views for 7 days: 5
Thanks to Best cliparts
source article: https://www.mikeash.com/pyblog/friday-qa-2012-08-31-obtaining-and-interpreting-image-data.html

by Mike Ash

Какава змяшчае некаторыя выдатныя абстракцыі для працы з выявамі. NSImage дазваляе апрацаваць выявы ў выглядзе непразрыстага шара, які Вы можаце проста намалюйце дзе вы хочаце яго. Вобраз Core абкручванні шмат апрацоўкі малюнкаў у просты ў выкарыстанні API, які вызваляе вас ад турботы аб тым, як асобныя пікселі прадстаўляюцца. Аднак, часам вы проста сапраўды хочаце атрымаць неапрацаваных піксельных дадзеных у код. Скот Лютэр прапанаваў сенняшняя тэма: выбарка і маніпуляванне, што неапрацаваныя піксельныя дадзеныя.

Тэорыя

Самы просты вобраз-уяўленне-гэта звычайны растравы. Гэта масіў бітаў, па адным на піксель, якое паказвае, ці з'яўляецца гэта чорны ці белы. Масіў ўтрымлівае радкі пікселяў адзін за адным, так, што агульная колькасць бітаў роўна шырыні малюнка, памножанай на вышыню. Вось прыклад растравага малюнка ўсмешлівай тварыкі:

    0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0
    0 0 1 0 0 1 0 0
    0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0
    0 1 0 0 0 0 1 0
    0 0 1 1 1 1 0 0
    0 0 0 0 0 0 0 0

Чыста чорны і белы колер не вельмі выразны сярэдні, вядома, і доступу да асобных бітам ў масіве трохі клопатаў. Давайце рухацца крок да выкарыстоўваючы адзін байт на піксель, што дазваляе чорна-белых танах (мы можам мець нулявую быць чорны, 255-белы, а лічбы ў паміж быць розныя адценні шэрага) і палягчае доступ да элементаў.

Яшчэ раз, мы будзем выкарыстоўваць масіў байтаў з паслядоўнымі шэрагамі. Вось некаторыя прыклады кода, каб вылучыць памяць для малюнкаў:

    uint8_t *AllocateImage(int width, int height)
    {
        return malloc(width * height);
    }

Каб дабрацца да канкрэтнага кропак з каардынатамі (x, y), мы павінны рухацца ўніз на Y шэрагаў, затым праз шэраг па х пікселяў. Паколькі шэрагі выкладваюцца паслядоўна, мы рухаемся ўніз па Y радкоў, перамяшчаючы праз масіў па Y * Шырыня байт. Індэкс для канкрэтнага піксела, тады х + Y * Шырыня. Зыходзячы з гэтага, тут знаходзяцца дзве функцыі для атрымання і ўстаноўкі паўтонавага пікселя ў пэўнай каардынаце:

    uint8_t ReadPixel(uint8_t *image, int width, int x, int y)
    {
        int index = x + y * width;
        return image[index];
    }

    void SetPixel(uint8_t *image, int width, int x, int y, uint8_t value)
    {
        int index = x + y * width;
        image[index] = value;
    }

Адценні шэрага-гэта яшчэ не ўсе, што цікава ў многіх выпадках, і мы хочам мець магчымасць прадстаўляць колеру. Тыповы спосаб прадстаўлення каляровых кропак з'яўляецца камбінацыяй трох значэнняў для чырвонага, зяленага і сіняга кампанентаў. Усе нулі вынікі ў чорнае, з іншымі каштоўнасцямі змешваючы гэтыя тры колеру разам, каб сфармаваць любы колер неабходны. Звычайна выкарыстоўваецца 8 біт на колер, што прыводзіць у 24 біты на піксель. Часам гэтыя спакаваныя разам, і часам яны на падкладцы з дадатковымі 8 бітамі пустэчы даць 32 біта на піксель, што прыемней працаваць з кампутарамі, як правіла, добрыя маніпуляваць 32-бітнымі значэннямі.

Празрыстасць, ці Альфа, можа быць карысны для прадстаўлення ў малюнку. 8 бітаў празрыстасці выдатна ўпісваецца ў 8 біт абіўка ў 32-бітны піксэл, і з дапамогай 32-бітных пікселяў холдынг чырвоны, зялены, сіні, і Альфа, верагодна, найбольш распаўсюджаны Фармат пікселяў ў цяперашні час.

Існуе два спосабу абнаўлення гэтых кропак адзін з адным. Агульны спосаб-проста запускаць іх усе разам у пэўнай паслядоўнасці, так ты б адзін байт чырвоны, зялены адзін байт, адзін байт-сіні, і адзін байт Альфа ўсе побач адзін з адным. Тады ў цябе будзе чырвоны, зялены, сіні, і Альфа для наступнага піксела, і гэтак далей. Кожны піксель займае чатыры сумежных байта памяці.

Акрамя таго, магчыма захоўваць кожны колер у асобны кавалак памяці. Кожны блок называецца плоскасць, і гэты фармат называецца "планар". У гэтым выпадку, вы ў асноўным маюць тры або чатыры (у залежнасці ад таго, ці з'яўляецца Альфа прысутнічае) абласцей памяці, кожная з якіх выкладваецца роўна як і пікселі ў адценні шэрага прыкладзе зверху. Піксель, колер-камбінацыя значэнняў ўсіх плоскасцяў. Гэта можа часам быць больш зручнай для працы, але часта павольней, з-за дрэннай лакальная спасылак, і часта больш складаныя, каб працаваць з, так што гэта значна менш распаўсюджаны Фармат.

Адзіная іншая рэч, каб высветліць, як колеру замовіла. Фармаце rgba (чырвоны, зялены, сіні, затым Альфа) заказ з'яўляецца найбольш распаўсюджаным на Mac, але заказы фармаце argb фармаце bgra і паказаць часам, як добра. Няма ніякай асаблівай прычыны, каб выбраць адзін над іншым, акрамя сумяшчальнасці або хуткасці. Каб пазбегнуць дарагога пераўтварэння фармату, лепш за ўсе, каб адпавядаць фармату выкарыстоўваецца ўсе, што вы будзеце маляваць, каб, эканомячы, або ад нагрузкі, калі гэта магчыма.

Атрымання Піксельных Дадзеных

Какава-class, які змяшчае і падае дадзеныя аб пікселях NSBitmapImageRep. Гэты падклас NSImageRep, які з'яўляецца абстрактным класам для аднаго "прадстаўлення" ладу. NSImage з'яўляецца кантэйнерам для аднаго або больш асобнікаў NSImageRep. У тым выпадку, калі есць больш чым адно ўяўленне, яны могуць прадстаўляць розныя памеры, рэзалюцый, каляровыя прасторы і т. п., і NSImage будзе выбраць самае лепшае адно для бягучага кантэксту пры маляванні.

Улічваючы, што, накшталт як, гэта павінна быць даволі легка атрымаць малюнак дадзеных з NSImage: знайсці NSBitmapImageRep у яе уяўленнях, то спытаеце, што прадстаўніцтва яго піксельныя дадзеныя.

Есць дзве праблемы з гэтым. Па-першае, малюнак можа не мець NSBitmapImageRep на ўсіх. Есць прадстаўленне тыпаў, якія не з'яўляюцца растравыя выявы. Напрыклад, NSImage, які прадстаўляе PDF будзе ўтрымліваць вектарныя дадзеныя, а не дадзеныя растравага малюнка, і выкарыстоўваць іншы тып прадстаўлення малюнкаў. Па-другое, нават калі малюнак мае NSBitmapImageRep, ніхто не ведае, што пиксельный Фармат, што ўяўленне будзе. Гэта не практычна, каб напісаць код для апрацоўкі ўсіх магчымых пиксельный Фармат, тым больш што гэта будзе складана тэставаць большасці выпадкаў.

Там вельмі шмат кода там, што робіць гэта ўсе роўна. Гэта сыходзіць з рук, робячы здагадкі пра змест NSImage і піксель Фармат NSBitmapImageRep. Гэта не надзейны, і яго варта пазбягаць.

Як надзейна атрымаць піксельныя дадзеныя, тады? Вы можаце намаляваць аб'ект NSImage надзейна, і вы можаце маляваць у NSBitmapImageRep выкарыстоўваючы NSGraphicsContext класа, і вы можаце атрымаць піксельныя дадзеныя ад NSBitmapImageRep. Ланцугу усе гэта разам, і вы можаце атрымаць піксельныя дадзеныя.

Вось некаторы код для апрацоўкі гэтай паслядоўнасці. Першае, што ен робіць гэта высветліць пікселяў шырыня і вышыня растравага прадстаўлення. Гэта не заўседы відавочна, як NSImage памер не адпавядае Памеры ў пікселях. Гэты код будзе выкарыстоўваць памер у любым выпадку, але ў залежнасці ад сітуацыі, вам можа спатрэбіцца выкарыстаць іншы спосаб даведацца памер:

    NSBitmapImageRep *ImageRepFromImage(NSImage *image)
    {
        int width = [image size].width;
        int height = [image size].height;

        if(width < 1 || height < 1)
            return nil;

Далей, мы ствараем NSBitmapImageRep. Гэта прадугледжвае выкарыстанне вельмі доўгіх метад инициализатора, які выглядае страшна, але я прайду ўсе параметры падрабязна:

        NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
                                 initWithBitmapDataPlanes: NULL
                                 pixelsWide: width
                                 pixelsHigh: height
                                 bitsPerSample: 8
                                 samplesPerPixel: 4
                                 hasAlpha: YES
                                 isPlanar: NO
                                 colorSpaceName: NSCalibratedRGBColorSpace
                                 bytesPerRow: width * 4
                                 bitsPerPixel: 32]

Давайце разгледзім гэтыя параметры па адным. Першы аргумент, BitmapDataPlanes, дазваляе вызначыць памяць, дзе піксельныя дадзеныя будуць захоўвацца. Перадача NULL вось, як гэты код робіць, кажа NSBitmapImageRep вылучыць сваю ўласную памяць ўнутраных, якія звычайна найбольш зручны спосаб справіцца з гэтым.

Наступны код задае Колькасць пікселяў у шырыню і максімум, што яно вылічана раней. Ен проста перадае гэтыя значэння ў для pixelsWide і pixelsHigh.

Цяпер мы трапляем у уласна Фармат пікселяў. Раней я згадаў, што 32-разрадным фармаце rgba (дзе чырвоны, зялены, сіні, і Альфа-займаюць адзін байт і размяшчаюцца паслядоўна ў памяці) - гэта распаўсюджаны Фармат пікселяў, і гэта тое, што мы збіраемся выкарыстоўваць. З кожнага ўзору складае адзін байт, то код праходзіць 8 для bitsPerSample:. У samplesPerPixel: параметр пазначае колькасць розных кампанентаў, якія выкарыстоўваюцца ў малюнку. У нас есць чатыры кампаненты (Р, Г, Б, І) І гэтак код праходзіць 4 тут.

У фармаце rgba есць Альфа, так што праходзім ды для hasAlpha. Мы не хочам планарны Фармат, так што праходзім не для isPlanar. Мы хочам, каляровая прастора RGB, так што праходзім NSCalibratedRGBColorSpace.

Далей, NSBitmapImageRep хоча ведаць, колькі байтаў складаюць для кожнай радкі малюнка. Гэта выкарыстоўваецца ў выпадку абіўка пажаданая. Часам малюнак радкі выкарыстоўвае больш строга мінімальная колькасць байт, звычайна па меркаваннях прадукцыйнасці, каб трымаць рэчы выраўнаваны прыгожа. Мы не хочам важдацца з пракладкай, так што праходзім мінімальная колькасць байтаў, неабходных для аднаго шэрагу пікселяў, што проста Шырыня * 4.

Нарэшце, яна запытвае лік бітаў на піксель. Па 8 біт на кампаненту і 4 кампаненты, гэта ўсяго толькі 32.

У нас цяпер есць NSBitmapImageRep з фарматам мы хочам, але як мы можам прыцягнуць у яго? Першы крок-гэта зрабіць NSGraphicsContext з ім:

        NSGraphicsContext *ctx = [NSGraphicsContext graphicsContextWithBitmapImageRep: rep];

Важнае Заўвага пры ліквідацыі непаладак: не ўсе параметры для NSBitmapImageRep прымальныя пры стварэнні NSGraphicsContext. Калі гэты радок скардзіцца на непадтрымоўваны Фармат, гэта азначае, што адным з параметраў, якія выкарыстоўваюцца для стварэння NSBitmapImageRep сістэме не па душы, таму калі ласка, вярніцеся назад і двойчы праверце гэтыя.

Наступны крок-усталяваць у гэтым кантэксце ў якасці бягучага графічнага кантэксту. Для таго, каб пераканацца, што мы не водимся з любой іншай графічнай дзейнасці, што мае месца быць, мы спачатку захаваць бягучы графічнае стан, таму мы можам аднавіць яго пазней:

        [NSGraphicsContext saveGraphicsState];
        [NSGraphicsContext setCurrentContext: ctx];

На дадзены момант, любы малюнак мы не будзем удавацца ў нашых новасьпечаных NSBitmapImageRep. Наступны крок-проста пацягнуць малюнак.

        [image drawAtPoint: NSZeroPoint fromRect: NSZeroRect operation: NSCompositeCopy fraction: 1.0];

NSZeroRect гэта проста зручны ярлык, які распавядае NSImage маляваць ўсе малюнак.

Цяпер, калі малюнак малюецца, мы выкінем графічнага кантэксту, каб пераканацца, што ні адзін з гэтай рэчы ўсе яшчэ стаялі ў чарзе, аднавіць графічнае стан, і вяртанне ў растравы малюнак:

        [ctx flushGraphics];
        [NSGraphicsContext restoreGraphicsState];

        return rep;
    }

Выкарыстоўваючы гэтую тэхніку, вы можаце атрымаць усе, што какава здольна звярнуць у зручную для 32-бітнага растравага малюнка ў rgba.

Інтэрпрэтаваць Піксельныя Дадзеныя

Цяпер, калі ў нас есць дадзеныя аб пікселях, што будзем рабіць з ім? Дакладна, што з ім рабіць, вырашаць вам, але давайце разгледзім наколькі рэальна атрымаць у піксельныя дадзеныя.

Давайце пачнем з вызначэння структуры для прадстаўлення асобных кропак:

    struct Pixel { uint8_t r, g, b, a; };

Гэта будзе выбудоўвацца з дапамогай фармату rgba піксельныя дадзеныя захоўваюцца ў NSBitmapImageRep. Мы можам узяць паказальнік з яго выкарыстоўваць:

    struct Pixel *pixels = (struct Pixel *)[rep bitmapData];

Доступ канкрэтнага кропак з каардынатамі (X, Y) і працуе так жа, як і ў папярэднім прыкладзе кода для малюнкаў у адценнях шэрага:

    int index = x + y * width;
    NSLog(@"Pixel at %d, %d: R=%u G=%u B=%u A=%u",
          x, y
          pixels[index].r,
          pixels[index].g,
          pixels[index].b,
          pixels[index].a);

Пераканайцеся, што X і Y знаходзяцца ў межах межаў малюнка перад гэтым, ці яшчэ той ці іншы эфект можа наступіць. Калі пашанцуе, за межы каардынатаў абрынецца.

Перабраць усе пікселі ў малюнку, просты парай для завес будзе рабіць:

    for(int y = 0; y < height; y++)
        for(int x = 0; x < width; x++)
        {
            int index = x + y * width;
            // Use pixels[index] here
        }

Звярніце ўвагу, як па восі Y, завесы крайнія адзін, хоць х спачатку будзе натуральны парадак. Гэта ж значна хутчэй перабраць пікселі ў тым жа парадку, у якім яны захоўваюцца ў памяці, так, што суседнія пікселі маюць доступ паслядоўна. Паклаўшы x ўнутры робіць гэта, і атрымліваецца код нашмат прыязней да кэш-памяці і кантролераў, якія ўбудаваныя ў ручкі паслядоўнага доступу.

Сучасны кампілятар, хутчэй за ўсе, генераваць добры код вышэй, але ў выпадку, калі вы параноік і хочаце, каб пераканацца, што кампілятар не будзе генераваць і памнажаем індэкс масіва на кожнай ітэрацыі цыклу, можна выканаць ітэрацыі праз арыфметычныя аперацыі над паказальнікамі замест:

    struct Pixel *cursor = pixels;
    for(int y = 0; y < height; y++)
        for(int x = 0; x < width; x++)
        {
            // Use cursor->r, cursor->g, etc.
            cursor++;
        }

Нарэшце, адзначым, што гэтыя дадзеныя змяняныя. Пры жаданні, вы можаце на самой справе змяніць Р, Г, Б, І, І NSBitmapImageRep будзе адлюстроўваць змены.

Выснова

Справа неапрацаваных піксельных дадзеных не тое, што вы звычайна трэба зрабіць, але калі вам гэта трэба, какава робіць яго адносна легка. Методыка з'яўляецца некалькі больш складаным, але, малюючы ў NSBitmapImageRep з вамі пиксельный Фармат, вы можаце атрымаць піксельныя дадзеныя ў фармаце па вашаму выбару. Як толькі ў вас есць, што піксельныя дадзеныя, гэта просты пытанне індэксацыі ў яе, каб атрымаць значэнні асобных кропак.

На сення гэта ўсе! Пятніца пытанні і адказы рухае чытача ідэі, як заўседы, так што калі ў вас есць якія-небудзь прапановы па тэмах вы б хацелі ўбачыць у наступным артыкуле, калі ласка, дасылайце іх.

Вам спадабалася гэтая артыкул? Я прадаю цэлую кнігу іх поўна. Яна даступная для iBooks і Kindle, плюс яго можна спампаваць у pdf і Фармат epub. Ен таксама даступны ў папяровай па-старому. Пстрыкніце тут для атрымання дадатковай інфармацыі.

paper4pc
Add a comment:
Sign in

See also

НАВУКА ПАЛКА

Навуковая палка

2015-11-09 16:36:42

Архіў рэцэнзій на кнігі, слупкі і каментары Фрэда Борца Сардэчна запрашаем у мой архіў...

Living Life Dangerously

Я веру ў жывой жыцця небяспечна

2015-11-02 17:01:23

"Я веру ў жывой жыцця небяспечна" (Прынц Валійскі) Я гулец. Я смелы гулец. Я азартны гулец. Я іду прама наперад. Мой...

Evariste Galois

Трагічная жыццё Эвариста Галуа, 1811-1832

2015-11-02 17:24:01

Чарцеж зрабіў у 1848 з памяці па Эварист брата Вялікую карціну Некаторыя спасылкі на сайты, расказваем пра жыцце Галуа. Геній і біеграфы: у...

Reverse Engineering Compiler

REC Studio 4 - адваротнае праектаванне кампілятара

2015-11-09 17:03:07

REC Studio інтэрактыўны декомпилятор. Ен чытае Windows, Лінукс, Мак АС х або сырыя выкананы файл, і спрабуе вырабіць кесарава як прадстаўленне...

Art for designers dkcoin8.com (DesignerKit)