Calculating luma of a rect in an image of .yuv format I'm trying to get the average luma of an area in an YUV image in the Android camera. I have come up with the following code which I think is correct and produces consistent results.

private static final int AVERAGE_LUMA_SAMPLE_SIZE = 2;

// sample the average luma
// yuvImage is the byte data return from a camera's previewFrame
public static int getAverageLuma(byte[] yuvImage, int imageWidth, int imageHeight, RectF sampleArea) {
    int totalLuma = 0;
    int totalSampled = 0;

    int top = (int)Math.max(sampleArea.top, 0);
    int left = (int)Math.max(sampleArea.left, 0);
    int right = (int)Math.min(sampleArea.right, imageWidth);
    int bottom = (int)Math.min(sampleArea.bottom, imageHeight);

    for(int i = top; i &lt; bottom; i += AVERAGE_LUMA_SAMPLE_SIZE) {
        int rowStartPosition = (i * imageWidth) + left;

        for (int j = 0; j &lt; right; j += AVERAGE_LUMA_SAMPLE_SIZE) {
            int bytePostion = rowStartPosition + j;             
            int y = (0xff &amp; ((int) yuvImage[bytePostion])) - 16;

            totalLuma += Math.max(y, 0);                
            totalSampled++;
        }
    }

    return Math.round((float)totalLuma / totalSampled);
}
One of the reasons that large 2d data sets are often reduced to a single 1D array is so that you can process the data in a single loop and the memory is contiguous. The trick to processing this data efficiently in a single loop is to be able to fiddle the single pointer in a way that makes sense...... and to do that, we need some smart pointer skips...

Consider the following single loop. It starts at the right place, and then skips the areas outside the sample rectangle....

int startpos = top * imageWidth + left;
int lastpos = bottom * imageWidth + right;
// skip is the amount of unsampled data from the right-side of the sample
// rectangle to the left side of the next row.
int skip = imageWidth - right + left;

// limit is a moving limit that we use to break-process the samples.
// limit only applies if we actually need to skip data.
int limit = skip == 0 ? lastpos + 1 : (top * imageWidth) + right;
for (int i = startpos; i &lt;= lastpos; i+= AVERAGE_LUMA_SAMPLE_SIZE ) {
    if (i &gt; limit) {
        // take the loop-increment in to consideration.
        i += skip - AVERAGE_LUMA_SAMPLE_SIZE;
        limit += imageWidth;
    } else {

        // Do the sampling work here.....


    }
}


Normally I am against updating a loop-counter index inside the loop, but there are times when it makes sense. This is one of those times.

Note, the skip variable is being calculated naively in this code. It should be smarter about taking the sample-size into consideration, and making sure it hits the beginning of the sample rectangle properly on the next line.... as it stands, if the rectangle is an odd-size, it may cause the start position of the next row to not be on the rectangle boundary. On average, I don't think this will make a difference, but that is your call.