Skip to content

Commit 8e78765

Browse files
Internal Change
PiperOrigin-RevId: 399964034
1 parent 6449120 commit 8e78765

File tree

9 files changed

+130
-64
lines changed

9 files changed

+130
-64
lines changed

tensorflow_lite_support/odml/java/image/src/com/google/android/odml/image/BitmapExtractor.java

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,14 @@ public final class BitmapExtractor {
3636
* conversions.
3737
*/
3838
public static Bitmap extract(MlImage image) {
39-
ImageContainer container = image.getContainer();
40-
switch (container.getImageProperties().getStorageType()) {
41-
case MlImage.STORAGE_TYPE_BITMAP:
42-
BitmapImageContainer bitmapImageContainer = (BitmapImageContainer) container;
43-
return bitmapImageContainer.getBitmap();
44-
case MlImage.STORAGE_TYPE_BYTEBUFFER:
45-
case MlImage.STORAGE_TYPE_MEDIA_IMAGE:
46-
// TODO(b/180504869): Support ByteBuffer -> Bitmap conversion.
47-
default:
48-
throw new IllegalArgumentException(
49-
"Extracting Bitmap from an MlImage created by objects other than Bitmap is not"
50-
+ " supported");
39+
ImageContainer imageContainer = image.getContainer(MlImage.STORAGE_TYPE_BITMAP);
40+
if (imageContainer != null) {
41+
return ((BitmapImageContainer) imageContainer).getBitmap();
42+
} else {
43+
// TODO(b/180504869): Support ByteBuffer -> Bitmap conversion.
44+
throw new IllegalArgumentException(
45+
"Extracting Bitmap from an MlImage created by objects other than Bitmap is not"
46+
+ " supported");
5147
}
5248
}
5349

tensorflow_lite_support/odml/java/image/src/com/google/android/odml/image/ByteBufferExtractor.java

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -75,25 +75,31 @@ public static ByteBuffer extract(MlImage image) {
7575
* conversions.
7676
*/
7777
static ByteBuffer extract(MlImage image, @ImageFormat int targetFormat) {
78-
ImageContainer container = image.getContainer();
79-
switch (container.getImageProperties().getStorageType()) {
80-
case MlImage.STORAGE_TYPE_BITMAP:
81-
BitmapImageContainer bitmapImageContainer = (BitmapImageContainer) container;
82-
return extractByteBufferFromBitmap(bitmapImageContainer.getBitmap(), targetFormat)
83-
.asReadOnlyBuffer();
84-
case MlImage.STORAGE_TYPE_BYTEBUFFER:
85-
ByteBufferImageContainer byteBufferImageContainer = (ByteBufferImageContainer) container;
86-
@ImageFormat int sourceFormat = byteBufferImageContainer.getImageFormat();
87-
if (sourceFormat == targetFormat) {
88-
return byteBufferImageContainer.getByteBuffer().asReadOnlyBuffer();
89-
}
90-
return convertByteBuffer(
91-
byteBufferImageContainer.getByteBuffer(), sourceFormat, targetFormat)
92-
.asReadOnlyBuffer();
93-
default:
94-
throw new IllegalArgumentException(
95-
"Extracting ByteBuffer from an MlImage created by objects other than Bitmap or"
96-
+ " Bytebuffer is not supported");
78+
ImageContainer container;
79+
ImageProperties byteBufferProperties =
80+
ImageProperties.builder()
81+
.setStorageType(MlImage.STORAGE_TYPE_BYTEBUFFER)
82+
.setImageFormat(targetFormat)
83+
.build();
84+
if ((container = image.getContainer(byteBufferProperties)) != null) {
85+
ByteBufferImageContainer byteBufferImageContainer = (ByteBufferImageContainer) container;
86+
return byteBufferImageContainer.getByteBuffer().asReadOnlyBuffer();
87+
} else if ((container = image.getContainer(MlImage.STORAGE_TYPE_BYTEBUFFER)) != null) {
88+
ByteBufferImageContainer byteBufferImageContainer = (ByteBufferImageContainer) container;
89+
@ImageFormat int sourceFormat = byteBufferImageContainer.getImageFormat();
90+
return convertByteBuffer(byteBufferImageContainer.getByteBuffer(), sourceFormat, targetFormat)
91+
.asReadOnlyBuffer();
92+
} else if ((container = image.getContainer(MlImage.STORAGE_TYPE_BITMAP)) != null) {
93+
BitmapImageContainer bitmapImageContainer = (BitmapImageContainer) container;
94+
ByteBuffer byteBuffer =
95+
extractByteBufferFromBitmap(bitmapImageContainer.getBitmap(), targetFormat)
96+
.asReadOnlyBuffer();
97+
image.addContainer(new ByteBufferImageContainer(byteBuffer, targetFormat));
98+
return byteBuffer;
99+
} else {
100+
throw new IllegalArgumentException(
101+
"Extracting ByteBuffer from an MlImage created by objects other than Bitmap or"
102+
+ " Bytebuffer is not supported");
97103
}
98104
}
99105

@@ -128,23 +134,24 @@ static Result create(ByteBuffer buffer, @ImageFormat int imageFormat) {
128134
* given {@code imageFormat}
129135
*/
130136
static Result extractInRecommendedFormat(MlImage image) {
131-
ImageContainer container = image.getContainer();
132-
switch (container.getImageProperties().getStorageType()) {
133-
case MlImage.STORAGE_TYPE_BITMAP:
134-
BitmapImageContainer bitmapImageContainer = (BitmapImageContainer) container;
135-
Bitmap bitmap = bitmapImageContainer.getBitmap();
136-
@ImageFormat int format = adviseImageFormat(bitmap);
137-
return Result.create(
138-
extractByteBufferFromBitmap(bitmap, format).asReadOnlyBuffer(), format);
139-
case MlImage.STORAGE_TYPE_BYTEBUFFER:
140-
ByteBufferImageContainer byteBufferImageContainer = (ByteBufferImageContainer) container;
141-
return Result.create(
142-
byteBufferImageContainer.getByteBuffer().asReadOnlyBuffer(),
143-
byteBufferImageContainer.getImageFormat());
144-
default:
145-
throw new IllegalArgumentException(
146-
"Extract ByteBuffer from an MlImage created by objects other than Bitmap or Bytebuffer"
147-
+ " is not supported");
137+
ImageContainer container;
138+
if ((container = image.getContainer(MlImage.STORAGE_TYPE_BITMAP)) != null) {
139+
Bitmap bitmap = ((BitmapImageContainer) container).getBitmap();
140+
@ImageFormat int format = adviseImageFormat(bitmap);
141+
Result result =
142+
Result.create(extractByteBufferFromBitmap(bitmap, format).asReadOnlyBuffer(), format);
143+
144+
image.addContainer(new ByteBufferImageContainer(result.buffer(), result.format()));
145+
return result;
146+
} else if ((container = image.getContainer(MlImage.STORAGE_TYPE_BYTEBUFFER)) != null) {
147+
ByteBufferImageContainer byteBufferImageContainer = (ByteBufferImageContainer) container;
148+
return Result.create(
149+
byteBufferImageContainer.getByteBuffer().asReadOnlyBuffer(),
150+
byteBufferImageContainer.getImageFormat());
151+
} else {
152+
throw new IllegalArgumentException(
153+
"Extract ByteBuffer from an MlImage created by objects other than Bitmap or Bytebuffer"
154+
+ " is not supported");
148155
}
149156
}
150157

tensorflow_lite_support/odml/java/image/src/com/google/android/odml/image/ImageProperties.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.android.odml.image.MlImage.StorageType;
2020
import com.google.android.odml.image.annotation.KeepForSdk;
2121
import com.google.auto.value.AutoValue;
22+
import com.google.auto.value.extension.memoized.Memoized;
2223

2324
/** Groups a set of properties to describe how an image is stored. */
2425
@AutoValue
@@ -40,6 +41,10 @@ public abstract class ImageProperties {
4041
@StorageType
4142
public abstract int getStorageType();
4243

44+
@Memoized
45+
@Override
46+
public abstract int hashCode();
47+
4348
/**
4449
* Creates a builder of {@link ImageProperties}.
4550
*

tensorflow_lite_support/odml/java/image/src/com/google/android/odml/image/MediaImageExtractor.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,12 @@ private MediaImageExtractor() {}
4141
* @throws IllegalArgumentException if the extraction failed.
4242
*/
4343
public static Image extract(MlImage image) {
44-
ImageContainer container = image.getContainer();
45-
switch (container.getImageProperties().getStorageType()) {
46-
case MlImage.STORAGE_TYPE_MEDIA_IMAGE:
47-
MediaImageContainer mediaImageContainer = (MediaImageContainer) container;
48-
return mediaImageContainer.getImage();
49-
default:
50-
throw new IllegalArgumentException(
51-
"Extract Media Image from an MlImage created by objects other than Media Image"
52-
+ " is not supported");
44+
ImageContainer container;
45+
if ((container = image.getContainer(MlImage.STORAGE_TYPE_MEDIA_IMAGE)) != null) {
46+
return ((MediaImageContainer) container).getImage();
5347
}
48+
throw new IllegalArgumentException(
49+
"Extract Media Image from an MlImage created by objects other than Media Image"
50+
+ " is not supported");
5451
}
5552
}

tensorflow_lite_support/odml/java/image/src/com/google/android/odml/image/MlImage.java

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@
1717

1818
import android.graphics.Rect;
1919
import androidx.annotation.IntDef;
20+
import androidx.annotation.Nullable;
2021
import com.google.android.odml.image.annotation.KeepForSdk;
2122
import java.io.Closeable;
2223
import java.lang.annotation.Retention;
2324
import java.lang.annotation.RetentionPolicy;
2425
import java.util.Collections;
26+
import java.util.HashMap;
2527
import java.util.List;
28+
import java.util.Map;
29+
import java.util.Map.Entry;
2630

2731
/**
2832
* Wraps image data for on-device machine learning (ODML) usages.
@@ -157,7 +161,9 @@ private synchronized void acquire() {
157161
public synchronized void close() {
158162
referenceCount -= 1;
159163
if (referenceCount == 0) {
160-
getContainer().close();
164+
for (ImageContainer imageContainer : containerMap.values()) {
165+
imageContainer.close();
166+
}
161167
}
162168
}
163169

@@ -200,7 +206,7 @@ public Internal getInternal() {
200206
return new Internal(this);
201207
}
202208

203-
private final ImageContainer container;
209+
private final Map<ImageProperties, ImageContainer> containerMap;
204210
private final int rotation;
205211
private final Rect roi;
206212
private final long timestamp;
@@ -212,7 +218,8 @@ public Internal getInternal() {
212218
/** Constructs an {@link MlImage} with a built container. */
213219
@KeepForSdk
214220
MlImage(ImageContainer container, int rotation, Rect roi, long timestamp, int width, int height) {
215-
this.container = container;
221+
this.containerMap = new HashMap<>();
222+
containerMap.put(container.getImageProperties(), container);
216223
this.rotation = rotation;
217224
this.roi = new Rect();
218225
this.roi.set(roi);
@@ -232,7 +239,40 @@ ImageContainer getContainer() {
232239
// According to the design, in the future we will support multiple containers in one image.
233240
// Currently just return the original container.
234241
// TODO(b/182443927): Cache multiple containers in MlImage.
235-
return container;
242+
return containerMap.values().iterator().next();
243+
}
244+
245+
/**
246+
* Gets container from required {@code storageType}. Returns {@code null} if not existed.
247+
*
248+
* <p>If there are multiple containers with required {@code storageType}, returns the first one.
249+
*/
250+
@Nullable
251+
@KeepForSdk
252+
ImageContainer getContainer(@StorageType int storageType) {
253+
for (Entry<ImageProperties, ImageContainer> entry : containerMap.entrySet()) {
254+
if (entry.getKey().getStorageType() == storageType) {
255+
return entry.getValue();
256+
}
257+
}
258+
return null;
259+
}
260+
261+
/** Gets container from required {@code imageProperties}. Returns {@code null} if non existed. */
262+
@Nullable
263+
@KeepForSdk
264+
ImageContainer getContainer(ImageProperties imageProperties) {
265+
return containerMap.get(imageProperties);
266+
}
267+
268+
/** Adds a new container if it doesn't exist. Returns {@code true} if it succeeds. */
269+
boolean addContainer(ImageContainer container) {
270+
ImageProperties imageProperties = container.getImageProperties();
271+
if (containerMap.containsKey(imageProperties)) {
272+
return false;
273+
}
274+
containerMap.put(imageProperties, container);
275+
return true;
236276
}
237277

238278
/**

tensorflow_lite_support/odml/java/image/tests/src/com/google/android/odml/image/BitmapMlImageBuilderTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public void build_fromBitmap_succeeds() {
3434
Bitmap bitmap = Bitmap.createBitmap(20, 25, Config.ARGB_8888);
3535

3636
MlImage image = new BitmapMlImageBuilder(bitmap).build();
37-
ImageContainer container = image.getContainer();
37+
ImageContainer container = image.getContainer(MlImage.STORAGE_TYPE_BITMAP);
3838

3939
assertThat(image.getWidth()).isEqualTo(20);
4040
assertThat(image.getHeight()).isEqualTo(25);

tensorflow_lite_support/odml/java/image/tests/src/com/google/android/odml/image/ByteBufferExtractorTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,16 @@ public void extract_rgbFromRgbaBitmap_succeeds() {
121121

122122
assertThat(result.isReadOnly()).isTrue();
123123
assertThat(result).isEquivalentAccordingToCompareTo(TestImageCreator.createRgbBuffer());
124+
125+
// Verifies ByteBuffer is cached inside MlImage.
126+
ByteBufferImageContainer byteBufferImageContainer =
127+
(ByteBufferImageContainer) image.getContainer(MlImage.STORAGE_TYPE_BYTEBUFFER);
128+
assertThat(byteBufferImageContainer.getByteBuffer()).isEqualTo(result);
129+
assertThat(byteBufferImageContainer.getImageFormat()).isEqualTo(MlImage.IMAGE_FORMAT_RGB);
130+
131+
// Verifies that extracted ByteBuffer is the cached one.
132+
ByteBuffer result2 = ByteBufferExtractor.extract(image, MlImage.IMAGE_FORMAT_RGB);
133+
assertThat(result2).isEqualTo(result);
124134
}
125135

126136
@Test
@@ -154,5 +164,16 @@ public void extractInRecommendedFormat_anyFormatFromRgbByteBuffer_succeeds() {
154164

155165
assertThat(result.buffer().isReadOnly()).isTrue();
156166
assertThat(result.format()).isEqualTo(MlImage.IMAGE_FORMAT_RGB);
167+
168+
// Verifies ByteBuffer is cached inside MlImage.
169+
ByteBufferImageContainer byteBufferImageContainer =
170+
(ByteBufferImageContainer) image.getContainer(MlImage.STORAGE_TYPE_BYTEBUFFER);
171+
assertThat(byteBufferImageContainer.getByteBuffer()).isEqualTo(result.buffer());
172+
assertThat(byteBufferImageContainer.getImageFormat()).isEqualTo(MlImage.IMAGE_FORMAT_RGB);
173+
174+
// Verifies that extracted ByteBuffer is the cached one.
175+
ByteBufferExtractor.Result result2 = ByteBufferExtractor.extractInRecommendedFormat(image);
176+
assertThat(result2.buffer()).isEqualTo(result.buffer());
177+
assertThat(result2.format()).isEqualTo(result.format());
157178
}
158179
}

tensorflow_lite_support/odml/java/image/tests/src/com/google/android/odml/image/ByteBufferMlImageBuilderTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public void build_fromByteBuffer_succeeds() {
3333
ByteBuffer buffer = ByteBuffer.allocate(500);
3434

3535
MlImage image = new ByteBufferMlImageBuilder(buffer, 20, 25, MlImage.IMAGE_FORMAT_RGB).build();
36-
ImageContainer container = image.getContainer();
36+
ImageContainer container = image.getContainer(MlImage.STORAGE_TYPE_BYTEBUFFER);
3737

3838
assertThat(image.getWidth()).isEqualTo(20);
3939
assertThat(image.getHeight()).isEqualTo(25);

tensorflow_lite_support/odml/java/image/tests/src/com/google/android/odml/image/MediaMlImageBuilderTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public void setUp() {
4949
@Test
5050
public void build_fromMediaImage_succeeds() {
5151
MlImage image = new MediaMlImageBuilder(mediaImage).build();
52-
ImageContainer container = image.getContainer();
52+
ImageContainer container = image.getContainer(MlImage.STORAGE_TYPE_MEDIA_IMAGE);
5353

5454
assertThat(image.getWidth()).isEqualTo(WIDTH);
5555
assertThat(image.getHeight()).isEqualTo(HEIGHT);

0 commit comments

Comments
 (0)