LensShadingMap.java revision 08c7116ab9cd04ad6dd3c04aa1017237e7f409ac
1/*
2 * Copyright (C) 2014 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
17package android.hardware.camera2.params;
18
19import static com.android.internal.util.Preconditions.*;
20import static android.hardware.camera2.params.RggbChannelVector.*;
21
22import android.hardware.camera2.CaptureResult;
23import android.hardware.camera2.utils.HashCodeHelpers;
24
25import java.util.Arrays;
26
27/**
28 * Immutable class for describing a {@code 4 x N x M} lens shading map of floats.
29 *
30 * @see CaptureResult#STATISTICS_LENS_SHADING_CORRECTION_MAP
31 */
32public final class LensShadingMap {
33
34    /**
35     * The smallest gain factor in this map.
36     *
37     * <p>All values in this map will be at least this large.</p>
38     */
39    public static final float MINIMUM_GAIN_FACTOR = 1.0f;
40
41    /**
42     * Create a new immutable LensShadingMap instance.
43     *
44     * <p>The elements must be stored in a row-major order (fully packed).</p>
45     *
46     * <p>This constructor takes over the array; do not write to the array afterwards.</p>
47     *
48     * @param elements
49     *          An array of elements whose length is
50     *          {@code RggbChannelVector.COUNT * rows * columns}
51     *
52     * @throws IllegalArgumentException
53     *            if the {@code elements} array length is invalid,
54     *            if any of the subelems are not finite or less than {@value #MINIMUM_GAIN_FACTOR},
55     *            or if rows or columns is not positive
56     * @throws NullPointerException
57     *            if {@code elements} is {@code null}
58     *
59     * @hide
60     */
61    public LensShadingMap(final float[] elements, final int rows, final int columns) {
62
63        mRows = checkArgumentPositive(rows, "rows must be positive");
64        mColumns = checkArgumentPositive(columns, "columns must be positive");
65        mElements = checkNotNull(elements, "elements must not be null");
66
67        if (elements.length != getGainFactorCount()) {
68            throw new IllegalArgumentException("elements must be " + getGainFactorCount() +
69                    " length, received " + elements.length);
70        }
71
72        // Every element must be finite and >= 1.0f
73        checkArrayElementsInRange(elements, MINIMUM_GAIN_FACTOR, Float.MAX_VALUE, "elements");
74    }
75
76    /**
77     * Get the number of rows in this map.
78     */
79    public int getRowCount() {
80        return mRows;
81    }
82
83    /**
84     * Get the number of columns in this map.
85     */
86    public int getColumnCount() {
87        return mColumns;
88    }
89
90    /**
91     * Get the total number of gain factors in this map.
92     *
93     * <p>A single gain factor contains exactly one color channel.
94     * Use with {@link #copyGainFactors} to allocate a large-enough array.</p>
95     */
96    public int getGainFactorCount() {
97        return mRows * mColumns * COUNT;
98    }
99
100    /**
101     * Get a single color channel gain factor from this lens shading map by its row and column.
102     *
103     * <p>The rows must be within the range [0, {@link #getRowCount}),
104     * the column must be within the range [0, {@link #getColumnCount}),
105     * and the color channel must be within the range [0, {@value RggbChannelVector#COUNT}).</p>
106     *
107     * <p>The channel order is {@code [R, Geven, Godd, B]}, where
108     * {@code Geven} is the green channel for the even rows of a Bayer pattern, and
109     * {@code Godd} is the odd rows.
110     * </p>
111     *
112     * @param colorChannel color channel from {@code [R, Geven, Godd, B]}
113     * @param column within the range [0, {@link #getColumnCount})
114     * @param row within the range [0, {@link #getRowCount})
115     *
116     * @return a gain factor >= {@value #MINIMUM_GAIN_FACTOR}
117     *
118     * @throws IllegalArgumentException if any of the parameters was out of range
119     *
120     * @see #RED
121     * @see #GREEN_EVEN
122     * @see #GREEN_ODD
123     * @see #BLUE
124     * @see #getRowCount
125     * @see #getColumnCount
126     */
127    public float getGainFactor(final int colorChannel, final int column, final int row) {
128        if (colorChannel < 0 || colorChannel > COUNT) {
129            throw new IllegalArgumentException("colorChannel out of range");
130        } else if (column < 0 || column >= mColumns) {
131            throw new IllegalArgumentException("column out of range");
132        } else if (row < 0 || row >= mRows) {
133            throw new IllegalArgumentException("row out of range");
134        }
135
136        return mElements[colorChannel + (row * mColumns +  column) * COUNT ];
137    }
138
139    /**
140     * Get a gain factor vector from this lens shading map by its row and column.
141     *
142     * <p>The rows must be within the range [0, {@link #getRowCount}),
143     * the column must be within the range [0, {@link #getColumnCount}).</p>
144     *
145     * @param column within the range [0, {@link #getColumnCount})
146     * @param row within the range [0, {@link #getRowCount})
147     *
148     * @return an {@link RggbChannelVector} where each gain factor >= {@value #MINIMUM_GAIN_FACTOR}
149     *
150     * @throws IllegalArgumentException if any of the parameters was out of range
151     *
152     * @see #getRowCount
153     * @see #getColumnCount
154     */
155    public RggbChannelVector getGainFactorVector(final int column, final int row) {
156        if (column < 0 || column >= mColumns) {
157            throw new IllegalArgumentException("column out of range");
158        } else if (row < 0 || row >= mRows) {
159            throw new IllegalArgumentException("row out of range");
160        }
161
162        final int offset = (row * mColumns +  column) * COUNT;
163
164        final float red =
165                mElements[RED + offset];
166        final float greenEven =
167                mElements[GREEN_EVEN + offset];
168        final float greenOdd =
169                mElements[GREEN_ODD + offset];
170        final float blue =
171                mElements[BLUE + offset];
172
173        return new RggbChannelVector(red, greenEven, greenOdd, blue);
174    }
175
176    /**
177     * Copy all gain factors in row-major order from this lens shading map into the destination.
178     *
179     * <p>Each gain factor will be >= {@link #MINIMUM_GAIN_FACTOR}.</p>
180     *
181     * @param destination
182     *          an array big enough to hold at least {@link RggbChannelVector#COUNT}
183     *          elements after the {@code offset}
184     * @param offset
185     *          a non-negative offset into the array
186     * @throws NullPointerException
187     *          If {@code destination} was {@code null}
188     * @throws IllegalArgumentException
189     *          If offset was negative
190     * @throws ArrayIndexOutOfBoundsException
191     *          If there's not enough room to write the elements at the specified destination and
192     *          offset.
193     *
194     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
195     */
196    public void copyGainFactors(final float[] destination, final int offset) {
197        checkArgumentNonnegative(offset, "offset must not be negative");
198        checkNotNull(destination, "destination must not be null");
199        if (destination.length + offset < getGainFactorCount()) {
200            throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
201        }
202
203        System.arraycopy(mElements, /*srcPos*/0, destination, offset, getGainFactorCount());
204    }
205
206    /**
207     * Check if this LensShadingMap is equal to another LensShadingMap.
208     *
209     * <p>Two lens shading maps are equal if and only if they have the same rows/columns,
210     * and all of their elements are {@link Object#equals equal}.</p>
211     *
212     * @return {@code true} if the objects were equal, {@code false} otherwise
213     */
214    @Override
215    public boolean equals(final Object obj) {
216        if (obj == null) {
217            return false;
218        }
219        if (this == obj) {
220            return true;
221        }
222        if (obj instanceof LensShadingMap) {
223            final LensShadingMap other = (LensShadingMap) obj;
224            return mRows == other.mRows
225                    && mColumns == other.mColumns
226                    && Arrays.equals(mElements, other.mElements);
227        }
228        return false;
229    }
230
231    /**
232     * {@inheritDoc}
233     */
234    @Override
235    public int hashCode() {
236        int elemsHash = HashCodeHelpers.hashCode(mElements);
237        return HashCodeHelpers.hashCode(mRows, mColumns, elemsHash);
238    }
239
240    /**
241     * Return the LensShadingMap as a string representation.
242     *
243     * <p> {@code "LensShadingMap{R:([%f, %f, ... %f], ... [%f, %f, ... %f]), G_even:([%f, %f, ...
244     *  %f], ... [%f, %f, ... %f]), G_odd:([%f, %f, ... %f], ... [%f, %f, ... %f]), B:([%f, %f, ...
245     *  %f], ... [%f, %f, ... %f])}"},
246     * where each {@code %f} represents one gain factor and each {@code [%f, %f, ... %f]} represents
247     * a row of the lens shading map</p>
248     *
249     * @return string representation of {@link LensShadingMap}
250     */
251    @Override
252    public String toString() {
253        StringBuilder str = new StringBuilder();
254        str.append("LensShadingMap{");
255
256        final String channelPrefix[] = {"R:(", "G_even:(", "G_odd:(", "B:("};
257
258        for (int ch = 0; ch < COUNT; ch++) {
259            str.append(channelPrefix[ch]);
260
261            for (int r = 0; r < mRows; r++) {
262                str.append("[");
263                for (int c = 0; c < mColumns; c++) {
264                    float gain = getGainFactor(ch, c, r);
265                    str.append(gain);
266                    if (c < mColumns - 1) {
267                        str.append(", ");
268                    }
269                }
270                str.append("]");
271                if (r < mRows - 1) {
272                    str.append(", ");
273                }
274            }
275
276            str.append(")");
277            if (ch < COUNT - 1) {
278                str.append(", ");
279            }
280        }
281
282        str.append("}");
283        return str.toString();
284    }
285
286    private final int mRows;
287    private final int mColumns;
288    private final float[] mElements;
289}
290