• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.mediapc;
18 
19 import com.beust.jcommander.IParameterValidator;
20 import com.beust.jcommander.JCommander;
21 import com.beust.jcommander.Parameter;
22 import com.beust.jcommander.ParameterException;
23 import com.beust.jcommander.Parameters;
24 
25 import java.io.FileInputStream;
26 import java.io.FileWriter;
27 import java.io.IOException;
28 import java.nio.ByteBuffer;
29 import java.nio.ByteOrder;
30 
31 /**
32  * This application reads an input yuv frame by frame and computes variance of all luma blocks
33  * (block size is 16x16) and prints their average to an output file. The yuv file is expected to
34  * be 8 bit or 10 bit with chroma sub sampling format being 2x2. As only luma blocks are
35  * considered for variance, the yuv can be 420 planar or 420 semi-planar. For 10 bit yuv, the
36  * input is expected to be in p010 format that is 10 bit sample data is stored in 2 bytes after
37  * left shifting by 6. The LSB 6 bits contains zero and bits from 6 - 16 contain the actual sample.
38  */
39 public class VarianceMain {
computeFrameVariance(int width, int height, Object luma)40     public static double computeFrameVariance(int width, int height, Object luma) {
41         final int bSize = 16;
42         double varianceSum = 0;
43         int blocks = 0;
44         for (int i = 0; i < height - bSize; i += bSize) {
45             for (int j = 0; j < width - bSize; j += bSize) {
46                 double sse = 0, sum = 0;
47                 int offset = i * width + j;
48                 for (int p = 0; p < bSize; p++) {
49                     for (int q = 0; q < bSize; q++) {
50                         int sample;
51                         if (luma instanceof byte[]) {
52                             sample = Byte.toUnsignedInt(((byte[]) luma)[offset + p * width + q]);
53                         } else if (luma instanceof short[]) {
54                             sample = Short.toUnsignedInt(((short[]) luma)[offset + p * width + q]);
55                             sample >>= 6;
56                         } else {
57                             throw new IllegalArgumentException("Unsupported data type");
58                         }
59                         sum += sample;
60                         sse += sample * sample;
61                     }
62                 }
63                 double meanOfSquares = sse / (bSize * bSize);
64                 double mean = sum / (bSize * bSize);
65                 double squareOfMean = mean * mean;
66                 double blockVariance = (meanOfSquares - squareOfMean);
67                 varianceSum += blockVariance;
68                 blocks++;
69             }
70         }
71         return (varianceSum / blocks);
72     }
73 
extractLuma(byte[] inputData)74     public static short[] extractLuma(byte[] inputData) {
75         int length = inputData.length / 2;
76         short[] p010Data = new short[length];
77         ByteBuffer.wrap(inputData)
78                 .order(ByteOrder.nativeOrder())
79                 .asShortBuffer()
80                 .get(p010Data);
81         return p010Data;
82     }
83 
84     @Parameters(separators = " =")
85     public static class Options {
86         @Parameter(names = "-f", description = "input yuv file path", required = true)
87         public String filePath;
88         @Parameter(names = "-o", description = "output txt file path")
89         public String outputFilename = "result.txt";
90         @Parameter(names = "-w", description = "input width", validateWith = SizeValidator.class,
91                 required = true)
92         public int width;
93         @Parameter(names = "-h", description = "input height", validateWith = SizeValidator.class,
94                 required = true)
95         public int height;
96         @Parameter(names = "-n", description = "number of frames to process")
97         public int numFrames = Integer.MAX_VALUE;
98         @Parameter(names = "-b", description = "input bit-depth", validateWith =
99                 BitDepthValidator.class, required = true)
100         public int bitDepth;
101         @Parameter(names = "--help", help = true)
102         public boolean help = false;
103     }
104 
105     public static class BitDepthValidator implements IParameterValidator {
validate(String name, String value)106         public void validate(String name, String value) throws ParameterException {
107             int n = Integer.parseInt(value);
108             if (n != 8 && n != 10) {
109                 throw new ParameterException(
110                         "Parameter " + name + " should be 8 or 10 (found " + value + ")");
111             }
112         }
113     }
114 
115     public static class SizeValidator implements IParameterValidator {
validate(String name, String value)116         public void validate(String name, String value) throws ParameterException {
117             int n = Integer.parseInt(value);
118             if (n < 0) {
119                 throw new ParameterException(
120                         "Parameter " + name + " should be positive (found " + value + ")");
121             } else if ((n & 1) != 0) {
122                 throw new ParameterException(
123                         "Parameter " + name + " should be even (found " + value + ")");
124             }
125         }
126     }
127 
main(String[] args)128     public static void main(String[] args) {
129         Options options = new Options();
130         JCommander jc = new JCommander(options);
131         try {
132             jc.parse(args);
133         } catch (Exception e) {
134             System.out.println(e.getMessage());
135             jc.usage();
136             System.exit(0);
137         }
138         if (options.help) {
139             jc.usage();
140             System.exit(0);
141         }
142         int bpp = options.bitDepth == 8 ? 1 : 2;
143         int ySize = options.width * options.height * bpp;
144         int uSize = (options.width / 2) * (options.height / 2) * bpp;
145         byte[] lumadata = new byte[ySize];
146         try (FileInputStream fileInputStream = new FileInputStream(options.filePath);
147              FileWriter writer = new FileWriter(options.outputFilename)) {
148             for (int i = 0; i < options.numFrames; i++) {
149                 int bytesRead = fileInputStream.read(lumadata);
150                 if (bytesRead == -1) break;
151                 if (bytesRead != ySize) {
152                     throw new IOException("Unexpected end of file or incorrect file format");
153                 }
154                 double variance;
155                 if (bpp == 2) {
156                     variance = computeFrameVariance(options.width, options.height,
157                             extractLuma(lumadata));
158                 } else {
159                     variance = computeFrameVariance(options.width, options.height, lumadata);
160                 }
161                 writer.write(i + " " + variance + "\n");
162                 long skipped = fileInputStream.skip(uSize + uSize);
163                 if (skipped != uSize + uSize) {
164                     throw new IOException("Unexpected end of file, expecting chroma samples");
165                 }
166             }
167         } catch (IOException e) {
168             System.out.println("Error opening file: " + e.getMessage());
169         }
170     }
171 }
172