Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(108)

Side by Side Diff: third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp

Issue 2502723002: ShapeDetection: implement barcode detection, blink part (Closed)
Patch Set: xianglu@ comments Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "modules/shapedetection/FaceDetector.h" 5 #include "modules/shapedetection/FaceDetector.h"
6 6
7 #include "core/dom/DOMException.h" 7 #include "core/dom/DOMException.h"
8 #include "core/dom/DOMRect.h"
9 #include "core/dom/Document.h"
10 #include "core/fetch/ImageResource.h"
11 #include "core/frame/ImageBitmap.h"
12 #include "core/frame/LocalDOMWindow.h" 8 #include "core/frame/LocalDOMWindow.h"
13 #include "core/frame/LocalFrame.h" 9 #include "core/frame/LocalFrame.h"
14 #include "core/html/HTMLImageElement.h"
15 #include "core/html/HTMLVideoElement.h"
16 #include "core/html/canvas/CanvasImageSource.h" 10 #include "core/html/canvas/CanvasImageSource.h"
17 #include "platform/graphics/Image.h"
18 #include "public/platform/InterfaceProvider.h"
19 #include "third_party/skia/include/core/SkImage.h"
20 #include "third_party/skia/include/core/SkImageInfo.h"
21 #include "wtf/CheckedNumeric.h"
22 11
23 namespace blink { 12 namespace blink {
24 13
25 namespace {
26
27 static CanvasImageSource* toImageSourceInternal(
28 const CanvasImageSourceUnion& value) {
29 if (value.isHTMLImageElement())
30 return value.getAsHTMLImageElement();
31
32 if (value.isImageBitmap() &&
33 !static_cast<ImageBitmap*>(value.getAsImageBitmap())->isNeutered()) {
34 return value.getAsImageBitmap();
35 }
36
37 if (value.isHTMLVideoElement())
38 return value.getAsHTMLVideoElement();
39
40 return nullptr;
41 }
42
43 } // anonymous namespace
44
45 FaceDetector* FaceDetector::create(ScriptState* scriptState) { 14 FaceDetector* FaceDetector::create(ScriptState* scriptState) {
46 return new FaceDetector(*scriptState->domWindow()->frame()); 15 return new FaceDetector(*scriptState->domWindow()->frame());
47 } 16 }
48 17
49 FaceDetector::FaceDetector(LocalFrame& frame) { 18 FaceDetector::FaceDetector(LocalFrame& frame) : ShapeDetector(frame) {}
50 DCHECK(!m_service.is_bound());
51 DCHECK(frame.interfaceProvider());
52 frame.interfaceProvider()->getInterface(mojo::GetProxy(&m_service));
53 }
54 19
55 ScriptPromise FaceDetector::detect(ScriptState* scriptState, 20 ScriptPromise FaceDetector::detect(ScriptState* scriptState,
56 const CanvasImageSourceUnion& imageSource) { 21 const CanvasImageSourceUnion& imageSource) {
57 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource); 22 return detectShapes(ShapeDetector::DetectorType::Face, scriptState,
haraken 2016/11/16 04:15:10 Would you make ScriptState* as the first parameter
mcasas 2016/11/16 05:23:10 Done.
58 23 imageSource);
59 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
60 ScriptPromise promise = resolver->promise();
61
62 if (!imageSourceInternal) {
63 // TODO(mcasas): Implement more CanvasImageSources, https://crbug.com/659138
64 NOTIMPLEMENTED() << "Unsupported CanvasImageSource";
65 resolver->reject(
66 DOMException::create(NotFoundError, "Unsupported source."));
67 return promise;
68 }
69
70 if (imageSourceInternal->wouldTaintOrigin(
71 scriptState->getExecutionContext()->getSecurityOrigin())) {
72 resolver->reject(
73 DOMException::create(SecurityError, "Source would taint origin."));
74 return promise;
75 }
76
77 if (imageSource.isHTMLImageElement()) {
78 return detectFacesOnImageElement(
79 resolver, static_cast<HTMLImageElement*>(imageSourceInternal));
80 }
81 if (imageSourceInternal->isImageBitmap()) {
82 return detectFacesOnImageBitmap(
83 resolver, static_cast<ImageBitmap*>(imageSourceInternal));
84 }
85 if (imageSourceInternal->isVideoElement()) {
86 return detectFacesOnVideoElement(
87 resolver, static_cast<HTMLVideoElement*>(imageSourceInternal));
88 }
89
90 NOTREACHED();
91 return promise;
92 }
93
94 ScriptPromise FaceDetector::detectFacesOnImageElement(
95 ScriptPromiseResolver* resolver,
96 const HTMLImageElement* img) {
97 ScriptPromise promise = resolver->promise();
98 if (img->bitmapSourceSize().isZero()) {
99 resolver->resolve(HeapVector<Member<DOMRect>>());
100 return promise;
101 }
102
103 ImageResource* const imageResource = img->cachedImage();
104 if (!imageResource || imageResource->errorOccurred()) {
105 resolver->reject(DOMException::create(
106 InvalidStateError, "Failed to load or decode HTMLImageElement."));
107 return promise;
108 }
109
110 Image* const blinkImage = imageResource->getImage();
111 if (!blinkImage) {
112 resolver->reject(DOMException::create(
113 InvalidStateError, "Failed to get image from resource."));
114 return promise;
115 }
116
117 const sk_sp<SkImage> image = blinkImage->imageForCurrentFrame();
118 DCHECK_EQ(img->naturalWidth(), static_cast<unsigned>(image->width()));
119 DCHECK_EQ(img->naturalHeight(), static_cast<unsigned>(image->height()));
120
121 if (!image) {
122 resolver->reject(DOMException::create(
123 InvalidStateError, "Failed to get image from current frame."));
124 return promise;
125 }
126
127 const SkImageInfo skiaInfo =
128 SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType());
129
130 const uint32_t allocationSize = skiaInfo.getSafeSize(skiaInfo.minRowBytes());
131
132 mojo::ScopedSharedBufferHandle sharedBufferHandle =
133 mojo::SharedBufferHandle::Create(allocationSize);
134 if (!sharedBufferHandle.is_valid()) {
135 DLOG(ERROR) << "Requested allocation : " << allocationSize
136 << "B, larger than |mojo::edk::kMaxSharedBufferSize| == 16MB ";
137 // TODO(xianglu): For now we reject the promise if the image is too large.
138 // But consider resizing the image to remove restriction on the user side.
139 // Also, add LayoutTests for this case later.
140 resolver->reject(
141 DOMException::create(InvalidStateError, "Image exceeds size limit."));
142 return promise;
143 }
144
145 const mojo::ScopedSharedBufferMapping mappedBuffer =
146 sharedBufferHandle->Map(allocationSize);
147
148 const SkPixmap pixmap(skiaInfo, mappedBuffer.get(), skiaInfo.minRowBytes());
149 if (!image->readPixels(pixmap, 0, 0)) {
150 resolver->reject(DOMException::create(
151 InvalidStateError,
152 "Failed to read pixels: Unable to decompress or unsupported format."));
153 return promise;
154 }
155
156 if (!m_service) {
157 resolver->reject(DOMException::create(
158 NotSupportedError, "Face detection service unavailable."));
159 return promise;
160 }
161
162 m_serviceRequests.add(resolver);
163 DCHECK(m_service.is_bound());
164 m_service->DetectFace(std::move(sharedBufferHandle), img->naturalWidth(),
165 img->naturalHeight(),
166 convertToBaseCallback(WTF::bind(
167 &FaceDetector::onDetectFace, wrapPersistent(this),
168 wrapPersistent(resolver))));
169 return promise;
170 }
171
172 ScriptPromise FaceDetector::detectFacesOnImageBitmap(
173 ScriptPromiseResolver* resolver,
174 ImageBitmap* imageBitmap) {
175 ScriptPromise promise = resolver->promise();
176 if (!imageBitmap->originClean()) {
177 resolver->reject(
178 DOMException::create(SecurityError, "ImageBitmap is not origin clean"));
179 return promise;
180 }
181
182 if (imageBitmap->size().area() == 0) {
183 resolver->resolve(HeapVector<Member<DOMRect>>());
184 return promise;
185 }
186
187 SkPixmap pixmap;
188 RefPtr<Uint8Array> pixelData;
189 uint8_t* pixelDataPtr = nullptr;
190 WTF::CheckedNumeric<int> allocationSize = 0;
191 // Use |skImage|'s pixels if it has direct access to them, otherwise retrieve
192 // them from elsewhere via copyBitmapData().
193 sk_sp<SkImage> skImage = imageBitmap->bitmapImage()->imageForCurrentFrame();
194 if (skImage->peekPixels(&pixmap)) {
195 pixelDataPtr = static_cast<uint8_t*>(pixmap.writable_addr());
196 allocationSize = pixmap.getSafeSize();
197 } else {
198 pixelData = imageBitmap->copyBitmapData(imageBitmap->isPremultiplied()
199 ? PremultiplyAlpha
200 : DontPremultiplyAlpha,
201 N32ColorType);
202 pixelDataPtr = pixelData->data();
203 allocationSize = imageBitmap->size().area() * 4 /* bytes per pixel */;
204 }
205
206 return detectFacesOnData(resolver, pixelDataPtr,
207 allocationSize.ValueOrDefault(0),
208 imageBitmap->width(), imageBitmap->height());
209 }
210
211 ScriptPromise FaceDetector::detectFacesOnVideoElement(
212 ScriptPromiseResolver* resolver,
213 const HTMLVideoElement* video) {
214 ScriptPromise promise = resolver->promise();
215
216 // TODO(mcasas): Check if |video| is actually playing a MediaStream by using
217 // HTMLMediaElement::isMediaStreamURL(video->currentSrc().getString()); if
218 // there is a local WebCam associated, there might be sophisticated ways to
219 // detect faces on it. Until then, treat as a normal <video> element.
220
221 // !hasAvailableVideoFrame() is a bundle of invalid states.
222 if (!video->hasAvailableVideoFrame()) {
223 resolver->reject(DOMException::create(
224 InvalidStateError, "Invalid HTMLVideoElement or state."));
225 return promise;
226 }
227
228 const FloatSize videoSize(video->videoWidth(), video->videoHeight());
229 SourceImageStatus sourceImageStatus = InvalidSourceImageStatus;
230 RefPtr<Image> image =
231 video->getSourceImageForCanvas(&sourceImageStatus, PreferNoAcceleration,
232 SnapshotReasonDrawImage, videoSize);
233
234 DCHECK_EQ(NormalSourceImageStatus, sourceImageStatus);
235
236 SkPixmap pixmap;
237 RefPtr<Uint8Array> pixelData;
238 uint8_t* pixelDataPtr = nullptr;
239 WTF::CheckedNumeric<int> allocationSize = 0;
240 // Use |skImage|'s pixels if it has direct access to them.
241 sk_sp<SkImage> skImage = image->imageForCurrentFrame();
242 if (skImage->peekPixels(&pixmap)) {
243 pixelDataPtr = static_cast<uint8_t*>(pixmap.writable_addr());
244 allocationSize = pixmap.getSafeSize();
245 } else {
246 // TODO(mcasas): retrieve the pixels from elsewhere.
247 NOTREACHED();
248 resolver->reject(DOMException::create(
249 InvalidStateError, "Failed to get pixels for current frame."));
250 return promise;
251 }
252
253 return detectFacesOnData(resolver, pixelDataPtr,
254 allocationSize.ValueOrDefault(0), image->width(),
255 image->height());
256 }
257
258 ScriptPromise FaceDetector::detectFacesOnData(ScriptPromiseResolver* resolver,
259 uint8_t* data,
260 int size,
261 int width,
262 int height) {
263 DCHECK(data);
264 DCHECK(size);
265 ScriptPromise promise = resolver->promise();
266
267 mojo::ScopedSharedBufferHandle sharedBufferHandle =
268 mojo::SharedBufferHandle::Create(size);
269 if (!sharedBufferHandle->is_valid()) {
270 resolver->reject(
271 DOMException::create(InvalidStateError, "Internal allocation error"));
272 return promise;
273 }
274
275 if (!m_service) {
276 resolver->reject(DOMException::create(
277 NotSupportedError, "Face detection service unavailable."));
278 return promise;
279 }
280
281 const mojo::ScopedSharedBufferMapping mappedBuffer =
282 sharedBufferHandle->Map(size);
283 DCHECK(mappedBuffer.get());
284
285 memcpy(mappedBuffer.get(), data, size);
286
287 m_serviceRequests.add(resolver);
288 DCHECK(m_service.is_bound());
289 m_service->DetectFace(std::move(sharedBufferHandle), width, height,
290 convertToBaseCallback(WTF::bind(
291 &FaceDetector::onDetectFace, wrapPersistent(this),
292 wrapPersistent(resolver))));
293 sharedBufferHandle.reset();
294 return promise;
295 }
296
297 void FaceDetector::onDetectFace(
298 ScriptPromiseResolver* resolver,
299 mojom::blink::FaceDetectionResultPtr faceDetectionResult) {
300 if (!m_serviceRequests.contains(resolver))
301 return;
302
303 HeapVector<Member<DOMRect>> detectedFaces;
304 for (const auto& boundingBox : faceDetectionResult->boundingBoxes) {
305 detectedFaces.append(DOMRect::create(boundingBox->x, boundingBox->y,
306 boundingBox->width,
307 boundingBox->height));
308 }
309
310 resolver->resolve(detectedFaces);
311 m_serviceRequests.remove(resolver);
312 } 24 }
313 25
314 DEFINE_TRACE(FaceDetector) { 26 DEFINE_TRACE(FaceDetector) {
315 visitor->trace(m_serviceRequests); 27 ShapeDetector::trace(visitor);
316 } 28 }
317 29
318 } // namespace blink 30 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698