Как я могу получить и установить значения пикселей изображения EmguCV Mat?

Я использую оболочку EmguCV 3.0.0 для библиотеки OpenCV 3.0. Я использую класс Mat в нескольких местах. Вот пример одноканального изображения 8x8, состоящего из double значений:

Mat image = new Mat(8, 8, DepthType.Cv64F, 1);

Класс Image<> предоставляет разумные средства для получения и установки значений пикселей, и этот метод идентичен для _ 5_, но это не кажется таким очевидным для _ 6_ класс. Единственный способ, которым я понял, как установить отдельный пиксель, - это использовать маску:

// set two pixel values, (0,0) to 9.0, (2, 3) to 42.0

Matrix<byte> mask = new Matrix<byte>(8,8);
mask.Data[0, 0] = 1;
image.SetTo(new MCvScalar(9.0), mask);

mask = new Matrix<byte>(8,8);
mask.Data[2, 3] = 1;
image.SetTo(new MCvScalar(42.0), mask);

Это кажется, как будто это должно быть две строки, а не шесть, поэтому я чувствую, что что-то упускаю. Все становится еще сложнее, когда Mat - это более одного канала, потому что Matrix<> только 2D, поэтому маска должна использоваться для установки пикселя на каждом канале.

Я не могу позволить себе время или память, чтобы установить пиксели таким образом. Как установить пиксели с помощью одного вызова метода?


person kdbanman    schedule 27.08.2015    source источник
comment
изображение [0,0] = 9; должен сделать это   -  person Miki    schedule 28.08.2015
comment
Я тоже этого ожидал. Но это не работает. Вот ошибка: Невозможно применить индексирование с помощью [] к выражению типа Emgu.CV.Mat   -  person kdbanman    schedule 28.08.2015


Ответы (3)


Вы можете получить элементы из Mat, скопировав блоки неуправляемой памяти с помощью DataPointer и преобразовав управляемые типы в неуправляемые. Значения настройки перемещаются в противоположном направлении.

В качестве примера вы можете использовать такой класс расширения

public static class MatExtension
{
    public static dynamic GetValue(this Mat mat, int row, int col)
    {
        var value = CreateElement(mat.Depth);
        Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 1);
        return value[0];
    }

    public static void SetValue(this Mat mat, int row, int col, dynamic value)
    {
        var target = CreateElement(mat.Depth, value);
        Marshal.Copy(target, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 1);
    }
    private static dynamic CreateElement(DepthType depthType, dynamic value)
    {
        var element = CreateElement(depthType);
        element[0] = value;
        return element;
    }

    private static dynamic CreateElement(DepthType depthType)
    {
        if (depthType == DepthType.Cv8S)
        {
            return new sbyte[1];
        }
        if (depthType == DepthType.Cv8U)
        {
            return new byte[1];
        }
        if (depthType == DepthType.Cv16S)
        {
            return new short[1];
        }
        if (depthType == DepthType.Cv16U)
        {
            return new ushort[1];
        }
        if (depthType == DepthType.Cv32S)
        {
            return new int[1];
        }
        if (depthType == DepthType.Cv32F)
        {
            return new float[1];
        }
        if (depthType == DepthType.Cv64F)
        {
            return new double[1];
        }
        return new float[1];
    }
}

Тогда получение и установка значения возможно одним вызовом метода

var row = 2;
var col = 1;
var mat = new Mat(3, 3, DepthType.Cv64F, 3);
mat.SetValue(row, col, 3.14);
var value = mat.GetValue(row, col);

Тесты с 200000000 операций показывают, что версия с динамическим типом может быть примерно в 2,5 раза медленнее, чем статическая.

public static double GetDoubleValue(this Mat mat, int row, int col)
{
    var value = new double[1];
    Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 1);
    return value[0];
}

public static void SetDoubleValue(this Mat mat, int row, int col, double value)
{
    var target = new[] { value };
    Marshal.Copy(target, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 1);
}
person Bartosz Rachwal    schedule 14.09.2015
comment
Интересное использование dynamic, которое выглядит очень удобным в использовании! Моя единственная проблема в том, что dynamic может быть от ~ 10x до ~ 100x медленнее, чем статически типизированные альтернативы. (см. здесь и здесь.) Ваши методы расширения должны быть в самом конце этого диапазона, потому что dynamic тип приема будет редко меняться. - person kdbanman; 14.09.2015
comment
Даже в этом случае оптимизированные Get/SetValue() методы, безопасно возвращающие / получающие double, должны быть возможны, потому что существует безопасное автоматическое преобразование из любого поддерживаемого Emgu DepthType в double. - person kdbanman; 14.09.2015
comment
Спасибо! Я этого не ожидал. Если сопровождающий EmguCV не сможет улучшить это, я скоро добавлю метод расширения, подобный вашему. - person kdbanman; 15.09.2015
comment
Бартош, отличный ответ. Только одна вещь вместо (row * mat.Cols + col) * mat ElementSize, я бы использовал ((row * mat.Step) + (col * mat.ElementSize)), поскольку шаг строки может не быть равен row * mat .Columns - person AeroClassics; 10.04.2019
comment
Сначала хороший ответ, мне очень помог. Но я не понимаю, как этот метод работает с несколькими каналами. В вашем примере вы написали var mat = new Mat(3, 3, DepthType.Cv64F, 3);, разве он не должен возвращать double [] размера 3, содержащий значение для каждого канала? - person Quergo; 19.11.2019
comment
так как, например, изменить пиксель на канале номер 3? - person peter bence; 20.02.2020
comment
@peterbence Просто добавьте 2 * sz, где sz = 1 для Cv8U до 8 для Cv64F (т.е. double) в зависимости от DepthType. - person timbo; 29.02.2020
comment
Кроме того, @AeroClassics прав. Ответ Бартоша не будет работать с прерывистыми матрицами (то есть подматрицами), если не используется исправление Aero. - person timbo; 29.02.2020

Основываясь на отличном ответе Бартоша Рахвала, я попытался написать его для OpenCvSharp:

    public static dynamic GetValue(this Mat mat, int row, int col)
    {
        var value = CreateElement(mat.Type());
        Marshal.Copy(mat.Data + (row * mat.Cols + col) * mat.ElemSize(), value, 0, 1);
        return value[0];
    }
    public static void SetValue(this Mat mat, int row, int col, dynamic value)
    {
        var target = CreateElement(mat.Type(), value);
        Marshal.Copy(target, 0, mat.Data + (row * mat.Cols + col) * mat.ElemSize(), 1);
    }
    private static dynamic CreateElement(MatType depthType, dynamic value)
    {
        var element = CreateElement(depthType);
        element[0] = value;
        return element;
    }
    private static dynamic CreateElement(MatType depthType)
    {
        switch (depthType)
        {
            case MatType.CV_8S:
                return new sbyte[1];
            case MatType.CV_8U:
                return new byte[1];
            case MatType.CV_16S:
                return new short[1];
            case MatType.CV_16U:
                return new ushort[1];
            case MatType.CV_32S:
                return new int[1];
            case MatType.CV_32F:
                return new float[1];
            case MatType.CV_64F:
                return new double[1];
            default:
                throw new NotImplementedException();
        }
    }
person Koray    schedule 26.01.2017
comment
Спасибо за ваши усилия, но я не думаю, что это здесь место. OpenCvSharp - это совершенно другая библиотека, чем та, о которой я спрашиваю. Если в OpenCvSharp нет существующего вопроса, на который можно было бы его переместить, подумайте о том, чтобы задать свой вопрос и ответить на него. - person kdbanman; 26.01.2017
comment
Оба являются оболочкой OpenCV, как видите, не сильно отличаются. Когда мне нужны знания о чем-либо, я ищу не только потоки OpenCvSharp. EMGU, Cpp, даже примеры Phton очень мне помогают. Думаю, новый вопрос будет пустой тратой времени. Это та же проблема и почти такой же код. Любой, кто ищет, как я, может им воспользоваться. Также стоит прочитать ваши дискуссии о динамике, любой, кто будет использовать это, также должен их прочитать. Спасибо. - person Koray; 27.01.2017
comment
С OpenCvSharp у вас есть метод Mat::Set<T>(x,y,val) для установки элемента в (x,y) на значение val. В обертке Emgu нет такого аксессуара. - person antoine; 20.03.2020

Это решение https://stackoverflow.com/a/32559496/15221325 для трех цветовых каналов по запросу пользователя Quergo:

Сначала хороший ответ, мне очень помог. Но я не понимаю, как этот метод работает с несколькими каналами. В вашем примере вы написали var mat = new Mat (3, 3, DepthType.Cv64F, 3); не должен ли он возвращать double [] размера 3, содержащий значение для каждого канала? - Quergo 18 нояб.

public static class MatExtension
{
    public static dynamic GetValues(this Mat mat, int row, int col)
    {
        var value = CreateElement3Channels(mat.Depth);
        Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 3);
        return value;
    }

    public static dynamic GetValue(this Mat mat, int channel, int row, int col)
    {
        var value = CreateElement3Channels(mat.Depth);
        Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 3);
        return value[channel];
    }

    public static dynamic GetValue(this Mat mat, int row, int col)
    {
        var value = CreateElement(mat.Depth);
        Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 1);
        return value[0];
    }

    public static void SetValues(this Mat mat, int row, int col, dynamic value)
    {
        Marshal.Copy(value, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 3);
    }

    public static void SetValue(this Mat mat, int channel, int row, int col, dynamic value)
    {
        var element = GetValues(mat, row, col);
        var target = CreateElement(element, value, channel);
        Marshal.Copy(target, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 3);
    }

    public static void SetValue(this Mat mat, int row, int col, dynamic value)
    {
        var target = CreateElement(mat.Depth, value);
        Marshal.Copy(target, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 1);
    }

    private static dynamic CreateElement(dynamic element, dynamic value, int channel)
    {
        element[channel] = value;
        return element;
    }

    private static dynamic CreateElement(DepthType depthType, dynamic value)
    {
        var element = CreateElement(depthType);
        element[0] = value;
        return element;
    }

    private static dynamic CreateElement3Channels(DepthType depthType)
    {
        if (depthType == DepthType.Cv8S)
        {
            return new sbyte[3];
        }

        if (depthType == DepthType.Cv8U)
        {
            return new byte[3];
        }

        if (depthType == DepthType.Cv16S)
        {
            return new short[3];
        }

        if (depthType == DepthType.Cv16U)
        {
            return new ushort[3];
        }

        if (depthType == DepthType.Cv32S)
        {
            return new int[3];
        }

        if (depthType == DepthType.Cv32F)
        {
            return new float[3];
        }

        if (depthType == DepthType.Cv64F)
        {
            return new double[3];
        }

        return new float[3];
    }

    private static dynamic CreateElement(DepthType depthType)
    {
        if (depthType == DepthType.Cv8S)
        {
            return new sbyte[1];
        }

        if (depthType == DepthType.Cv8U)
        {
            return new byte[1];
        }

        if (depthType == DepthType.Cv16S)
        {
            return new short[1];
        }

        if (depthType == DepthType.Cv16U)
        {
            return new ushort[1];
        }

        if (depthType == DepthType.Cv32S)
        {
            return new int[1];
        }

        if (depthType == DepthType.Cv32F)
        {
            return new float[1];
        }

        if (depthType == DepthType.Cv64F)
        {
            return new double[1];
        }

        return new float[1];
    }
}
person eng3ls    schedule 16.02.2021
comment
Вы только что дали ссылку на другой ответ на тот же вопрос? Мы ценим расширенное решение, но наличие трех цветовых каналов не имело значения для исходного вопроса. - person Connor Low; 16.02.2021
comment
Процитирую другой ответ: сначала хороший ответ мне очень помог. Но я не понимаю, как этот метод работает с несколькими каналами. В вашем примере вы написали var mat = new Mat (3, 3, DepthType.Cv64F, 3); не должен ли он возвращать double [] размера 3, содержащий значение для каждого канала? - Quergo 18 нояб., 22:30 Таким образом, определенно есть интерес к 3 цветовым каналам, и в верхнем ответе есть трехцветное изображение, что в данном случае было бы ошибкой. - person eng3ls; 17.02.2021
comment
Привет, eng3ls, из вашего ответа не было ясно, что вы обращались к комментарию другого пользователя. Я рекомендую обновить свой ответ, объяснив это. Примечание: пользователи, которые голосуют за что-то, не могут изменить свой голос на следующий день , если сообщение не отредактировано. - person Connor Low; 17.02.2021
comment
Я отредактировал свое решение, указав, что обращаюсь к комментарию другого пользователя. Поскольку у меня недостаточно репутации, чтобы прокомментировать лучшее решение, вы можете написать здесь комментарий, что на вопрос Quergo был дан ответ ... ему 2 года, поэтому я не думаю, что это действительно имеет значение, но вы никогда не узнаете. Однако пользователь peter bence также задавал тот же вопрос в прошлом году. - person eng3ls; 20.04.2021