получить среднее значение красного канала из растрового изображения с помощью Renderscript android

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

Сценарий:

int *sum;

uchar4 __attribute__ ((kernel)) invert(uchar4 in, uint32_t x, uint32_t y){
uchar4 out = in;
sum[0] += in.r;
out.r = 255 - in.r; 
out.g = 255 - in.g;
out.b = 255 - in.b;
return out;
}

Итак, то, что я пытаюсь сделать выше, - это суммировать все красные значения в указателе «сумма» и в коде Java:

int [] sum = new int[2];
Allocation data = Allocation.createSized(rs, Elelement.I32(rs), sum.length, Allocation.USAGE_SCRIPT);
data.copy1DRangeFrom(0, sum.length, sum);

Bitmap output = Bitmap.createBitmap(input.getWith(), input.getHeight(), input.getConfig);
Allocation in = Allocation.createFromBitmap(rs, input);
Allocation out = Allocation.createFromBitmap(rs, output);

ScriptC_root root = new ScriptC_root(rs);
root.bind_sum(sum);
root.forEach_invert(in, out);
out.copyTo(output);
data.copyTo(sum); //Here is where i am trying to geck the sum

//so i try to compute the average 
float avg = sum[0] / input.getWidth() * input.getHeight();

Для операции инвертирования растрового изображения я получаю в точности ожидаемый результат
Значение, которое я получаю в среднем, слишком мало (менее 150), в то время как входное растровое изображение представляет собой полностью красное изображение.
Я попытался просто увеличить указатель * sum в скрипте, чтобы проверить, обращается ли цикл foEach к одному и тому же количеству пикселей каждый раз, и при каждом запуске я получаю разные числа.
Помощь о том, как это сделать правильно, будет очень кстати.


person Milorenus Lomaliza    schedule 12.08.2016    source источник


Ответы (1)


Примечание. этот код строго связан с основным вопросом, как получить среднее значение канала.

То, чего вы хотите достичь, можно сделать следующим образом:

1) RenderScript

#pragma rs java_package_name(net.hydex11.channelaverageexample)
#pragma rs_fp_relaxed
#pragma version(1)

// Use two global counters
static int totalSum = 0;
static int counter = 0;

// One kernel just sums up the channel red value and increments 
// the global counter by 1 for each pixel
void __attribute__((kernel)) addRedChannel(uchar4 in){

 rsAtomicAdd(&totalSum, in.r);
 rsAtomicInc(&counter);

}

// This kernel places, inside the output allocation, the average
int __attribute__((kernel)) getTotalSum(int x){
    return totalSum/counter;
}

void resetCounters(){

    totalSum = 0;
    counter = 0;

}

Примечание. Я использовал функции rsAtomic*, потому что, если вы работаете с глобальной переменной из разных потоков, вы должны использовать потокобезопасные операции (например, Безопасность потоков).

2) сторона Java

private void example() {

    RenderScript mRS = RenderScript.create(this);

    // Loads example image
    Bitmap inputImage = BitmapFactory.decodeResource(getResources(), R.drawable.houseimage);
    Allocation inputAllocation = Allocation.createFromBitmap(mRS, inputImage);

    // Allocation where to store the sum result (for output purposes)
    Allocation sumAllocation = Allocation.createSized(mRS, Element.I32(mRS), 1);

    // Init script
    ScriptC_average scriptC_average = new ScriptC_average(mRS);

    // If you have a cycle, you have to reset the counters on each cycle
    //scriptC_average.invoke_resetCounters();

    // 1. Execute sum kernel
    scriptC_average.forEach_addRedChannel(inputAllocation);

    // 2. Execute a kernel that copies the sum into an output allocation
    scriptC_average.forEach_getTotalSum(sumAllocation);

    int sumArray[] = new int[1];
    sumAllocation.copyTo(sumArray);

    // E.g. simple output can be 66
    Log.d("AverageExample", String.format("The average of red channel is %d", sumArray[0]));

}

Ссылка: RenderScript: простой способ параллельных вычислений на Android

person cmaster11    schedule 12.08.2016
comment
Большое спасибо за то, что нашли время ответить на мой вопрос, и ваш код работает хорошо. Дело в том, что я использую этот код в приложении (измерьте частоту сердечных сокращений), где я получаю новый кадр каждые 50 миллисекунд и вычисляю среднее значение этого канала, используя ваш код (еще раз спасибо). Но, похоже, у меня проблема с распределением памяти. После запуска нескольких кадров он возвращает эту ошибку: 08-12 18: 40: 12.886 18448-19552 / com.example.aa.rendersrc V / RenderScript: com.example.aa.rendersrc A / libc: mmap failed - person Milorenus Lomaliza; 12.08.2016
comment
Собственно, код, который я написал, рассчитан на один запуск. Если вы хотите использовать его в цикле, посмотрите только что добавленную мной resetCounters функцию. - person cmaster11; 12.08.2016
comment
Спасибо за все, исправил ошибку :) - person Milorenus Lomaliza; 12.08.2016
comment
Извините, что задаю еще раз, у меня последний вопрос. Я хочу получить среднее значение в виде числа с плавающей запятой, чтобы получить большую точность. Я изменил totalSum на float, а также функцию getTotalSum на float, я также изменил тип элемента на F32 при инициализации объекта распределения, чтобы не было ошибок. просто в результате я все время получаю 0. Есть идеи, что я сделал не так? - person Milorenus Lomaliza; 12.08.2016
comment
Вы можете просто привести значения, так что (float)totalSum/(float)counter. Таким образом, вы не получите 0. - person cmaster11; 12.08.2016