Nano Hash - криптовалюты, майнинг, программирование

iOS, как рассчитать количество пикселей / площадь, заключенную в кривую?

Я получил кривую произвольной формы, охватывающую некоторую область. Я хотел бы приблизительно определить количество пикселей, которое охватывает кривая на экране iPhone/iPad. Как это сделать?

  • Кривая определяется как последовательные координаты x/y точек.
  • Кривая замкнута.
  • Кривая рисуется касаниями пользователя (метод touchesMoved), и я не знаю, как она выглядит.

введите здесь описание изображения

Думал как-то заполнить замкнутую кривую цветом, потом вычислить количество пикселей этого цвета на скриншоте экрана. Это означает, что мне нужно знать, как программно заполнить замкнутую кривую цветом.

Есть ли какой-то другой способ, о котором я не думаю?

Спасибо!


  • Алгоритм заливки гугла. 08.01.2013
  • Кроме того, что вы можете сделать, это численное интегрирование. 08.01.2013
  • Смежны ли координаты X, Y, составляющие вашу кривую? Или кривая представляет собой набор малых векторов? 08.01.2013
  • выглядит как вопрос в одном из моих последних интервью на должность iOS-разработчика: Реализовать алгоритм флуда — на Java. В Яве? Я думал, это собеседование на должность iOS. Почему Ява? Мы только хотим, чтобы разработчики могли делать все — и мне нужно иметь возможность проверить ваш ответ 12.01.2013

Ответы:


1

Давайте сделаем это, создав путь Quartz, охватывающий вашу кривую. Затем мы создадим растровый контекст и заполним путь в этом контексте. Затем мы можем изучить растровое изображение и подсчитать заполненные пиксели. Обернем все это в удобную функцию:

static double areaOfCurveWithPoints(const CGPoint *points, size_t count) {

Сначала нам нужно создать путь:

    CGPathRef path = createClosedPathWithPoints(points, count);

Затем нам нужно получить ограничивающую рамку пути. Координаты CGPoint не обязательно должны быть целыми числами, но растровое изображение должно иметь целые размеры, поэтому мы получим целостную ограничивающую рамку, по крайней мере, такую ​​же большую, как ограничивающая рамка пути:

    CGRect frame = integralFrameForPath(path);

Нам также нужно решить, насколько широким (в байтах) будет растровое изображение:

    size_t bytesPerRow = bytesPerRowForWidth(frame.size.width);

Теперь мы можем создать растровое изображение:

    CGContextRef gc = createBitmapContextWithFrame(frame, bytesPerRow);

Растровое изображение заполняется черным цветом при его создании. Мы заполним путь белым цветом:

    CGContextSetFillColorWithColor(gc, [UIColor whiteColor].CGColor);
    CGContextAddPath(gc, path);
    CGContextFillPath(gc);

Теперь мы закончили с путем, поэтому мы можем освободить его:

    CGPathRelease(path);

Далее мы вычислим площадь, которая была заполнена:

    double area = areaFilledInBitmapContext(gc);

Теперь мы закончили с растровым контекстом, так что мы можем освободить его:

    CGContextRelease(gc);

Наконец, мы можем вернуть вычисленную площадь:

    return area;
}

Что ж, это было легко! Но мы должны написать все эти вспомогательные функции. Начнем сверху. Создание пути тривиально:

static CGPathRef createClosedPathWithPoints(const CGPoint *points, size_t count) {
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddLines(path, NULL, points, count);
    CGPathCloseSubpath(path);
    return path;
}

Получение интегральной ограничивающей рамки пути также тривиально:

static CGRect integralFrameForPath(CGPathRef path) {
    CGRect frame = CGPathGetBoundingBox(path);
    return CGRectIntegral(frame);
}

Чтобы выбрать количество байтов на строку растрового изображения, мы могли бы просто использовать ширину ограничивающей рамки пути. Но я думаю, что Quartz нравится иметь растровые изображения, кратные хорошей степени двойки. Я не проводил никаких тестов на этом, так что вы можете поэкспериментировать. Сейчас мы округлим ширину до следующего наименьшего числа, кратного 64:

static size_t bytesPerRowForWidth(CGFloat width) {
    static const size_t kFactor = 64;
    // Round up to a multiple of kFactor, which must be a power of 2.
    return ((size_t)width + (kFactor - 1)) & ~(kFactor - 1);
}

Мы создаем растровый контекст с вычисленными размерами. Нам также нужно перевести начало системы координат. Почему? Потому что начало ограничивающей рамки пути может быть не в (0, 0).

static CGContextRef createBitmapContextWithFrame(CGRect frame, size_t bytesPerRow) {
    CGColorSpaceRef grayscale = CGColorSpaceCreateDeviceGray();
    CGContextRef gc = CGBitmapContextCreate(NULL, frame.size.width, frame.size.height, 8, bytesPerRow, grayscale, kCGImageAlphaNone);
    CGColorSpaceRelease(grayscale);
    CGContextTranslateCTM(gc, -frame.origin.x, -frame.origin.x);
    return gc;
}

Наконец, нам нужно написать хелпер, который фактически подсчитывает заполненные пиксели. Мы должны решить, как мы хотим считать пиксели. Каждый пиксель представлен одним 8-битным целым числом без знака. Черный пиксель равен 0. Белый пиксель равен 255. Цифры между ними — это оттенки серого. Quartz сглаживает края пути, когда он заполняет его серыми пикселями. Итак, нам нужно решить, как считать эти серые пиксели.

Один из способов — определить порог, например 128. Любой пиксель на пороге или выше считается заполненным; остальные считаются незаполненными.

Другой способ — считать серые пиксели частично заполненными и суммировать это частичное заполнение. Таким образом, два заполненных ровно наполовину пикселя объединяются и считаются одним полностью заполненным пикселем. Сделаем так:

static double areaFilledInBitmapContext(gc) {
    size_t width = CGBitmapContextGetWidth(gc);
    size_t height = CGBitmapContextGetHeight(gc);
    size_t stride = CGBitmapContextGetBytesPerRow(gc);
    uint8_t *pixels = CGBitmapContextGetData(gc);
    uint64_t coverage = 0;
    for (size_t y = 0; y < height; ++y) {
        for (size_t x = 0; x < width; ++x) {
            coverage += pixels[y * stride + x];
        }
    }
    return (double)coverage / UINT8_MAX;
}

Вы можете найти весь код в этой сути.

11.01.2013

2

Я бы взял рисунок как CGIMage...

(CGBitmapContextCreateImage(UIGraphicsGetCurrentContext());

Затем, как рекомендовано выше, используйте подход «Заливка заливкой» для подсчета пикселей. (гугл заливка)

08.01.2013
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

Как написать эффективное резюме
Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..