מדריך Windows Phone – עבודה עם המצלמה
קרדיט: פרק זה נכתב ע"י אלכס גולש, יועץ בכיר בקבוצת סלע ו Silverlight MVP.
הקדמה
כל מכשירי Windows Phone מצוידים במצלמה בעלת רזולוציה מינימלית של 5 מגה פיקסל. עבורנו המפתחים, גישה למצלמה מאפשרת שלל אפשרויות כמו זיהוי פנים, וידיאו-צ'אט, יישומי Augmented Reality ועוד. מתחילה, בגרסה הראשונה של מערכת ההפעלה מכשירי Windows Phone תמכו בתרחישים של שימוש במצלמה דרך Launchers ו- Choosers (ראה פרק מתאים במדריך). ה- API סיפק לנו את CameraCaptureTask המאפשר לצלם תמונה ולשלב באפליקציה. במאמר זה לא נעסוק בתרחיש הזה של שימוש בממשק של המצלמה, במקום זאת, נעסוק ב- Live Camera Input.
Live Camera Feed – הזנה מהמצלמה בזמן אמת
על מנת לקבל הזנה בזמן אמת מהמצלמה ולהצליח לייצר מופע מהמחלקה CaptureSource האפליקציה שלנו חייבת להגדיר את היכולות הבאות ב- WMAppManifest.xml:
<Capability Name="ID_CAP_ISV_CAMERA"/>
<Capability Name="ID_CAP_MICROPHONE"/>
הקינפוג של CaptureSource מתבצע באופן דומה לזה של אותה מחלקה הקיימת גם ב Silverlight 4 for Desktop:
if (CaptureDeviceConfiguration.AllowedDeviceAccess ||
CaptureDeviceConfiguration.RequestDeviceAccess())
{
if (null == captureSource)
{
captureSource = new CaptureSource();
captureSource.VideoCaptureDevice =
CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
captureSource.AudioCaptureDevice =
CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice();
previewVideo.SetSource(captureSource);
}
captureSource.CaptureImageCompleted += captureSource_CaptureImageCompleted;
captureSource.CaptureFailed += captureSource_CaptureFailed;
captureSource.Start();
}
קטע הקוד הזה מגדיר את המצלמה של המכשיר כמקור של VideoCaptureDevice, מאפשר להגדיר את מקור האודיו של AudioCaptureDevice למיקרופון של הטלפון ונרשם לאירועי המצלמה. בנוסף לכך, נעשה שימוש בהזנה של CaptureSource כמקור ל- VideoBrush שניתן להשתמש בו על מנת לתת Fill וידיאו לכל אלמנט גרפי במסך.
כאשר המשתמש מצלם תמונה ומתקבל האירוע CaptureImageCompleted אנחנו יכולים לשמור את התמונה שצולמה בספריית התמונות של המשתמש ולהציגה על המסך:
void captureSource_CaptureImageCompleted(object sender,
CaptureImageCompletedEventArgs e)
{
if (null == e.Error)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
MediaLibrary mediaLibrary = new MediaLibrary();
string fileName = string.Format("{0:yyyy-MM-dd-HH-mm-ss}.jpg", DateTime.Now);
MemoryStream stream = new MemoryStream();
e.Result.SaveJpeg(stream, e.Result.PixelWidth,
e.Result.PixelHeight, 0, 100);
stream.Position = 0;
mediaLibrary.SavePicture(fileName, stream);
previewImages.Add(e.Result);
});
}
}
קטע הקוד שומר את התמונה שצולמה כקובץ JPEG בספריית התמונות של המשתמש ומגדיר אותו כמקור של previewImage על המסך.
PhotoCamera
דרך נוספת לקבל הזנה ישירה מהמצלמה היא שימוש במחלקה PhotoCamera. על-מנת לעשות שימוש בגישה זו עלינו לאתחל מופע של PhotoCamera מתוך Microsoft.Devices. בנוסף, על-מנת להשתמש במצלמה מתוך אפליקציה עלינו להגדיר את ID_CAP_ISV_CAMERA ב- WMAppManifest.xml:
<Capability Name="ID_CAP_ISV_CAMERA"/>
המחלקה PhotoCamera מייצגת פונקציונליות בסיסית של המצלמה עבור מצלמת הסטילס של Windows Phone ומאפשרת למפתחים לקנפג את האפשרויות של המצלמה בתחומי רזולוציה, מבזק ופוקוס. על-מנת לפתח אפליקציה שעושה שימוש במצלמה עלינו להצטייד במכשיר אמיתי, שכן האמולטור של Windows Phone תומך ב- APIs אך לא מספק אמולציה של כפתורי החומרה או משוב ויזואלי מהמצלמה. האמולטור מציג מלבן לבן שבתוכו מלבן שחור קטן:
בכדי להשתמש ב- PhotoCamera הוא חייב להיות מוגדר כמקור של משטח ציור כלשהו. בקטע הקוד הבא אנחנו משתמשים באובייקט VideoBrush בכדי "לצייר" באמצעות הזנה ישירה מהמצלמה. מלבן שתכונת ה-Fill שלו משויכת ל- VideoBrush מכסה את המשטח העיקרי של האפליקציה שלנו:
<Rectangle x:Name="rectPreview"
Width="780"
Height="460"
Margin="10">
<Rectangle.Fill>
<VideoBrush x:Name="previewVideo" />
</Rectangle.Fill>
</Rectangle>
בקטע הקוד הבא אנחנו מאתחלים את PhotoCamera ומשייכים למקור שלו את ה- VideoBrush שנקרא previewVideo, נבצע זאת בתוך האירוע OnNavigatedTo:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
photoCamera = new PhotoCamera();
photoCamera.Initialized += photoCamera_Initialized;
previewVideo.SetSource(photoCamera);
base.OnNavigatedTo(e);
}
איתחולו של אובייקט PhotoCamera לוקח זמן מה לכן רוב הפונקציונאליות תהיה זמינה רק באירוע Initialized אליו נרשמנו בקטע הקוד הקודם:
void photoCamera_Initialized(object sender, CameraOperationCompletedEventArgs e)
{
if (photoCamera.IsFlashModeSupported(FlashMode.Auto))
{
photoCamera.FlashMode = FlashMode.Auto;
}
photoCamera.Resolution = photoCamera.AvailableResolutions.ElementAt(0);
photoCamera.AutoFocusCompleted += photoCamera_AutoFocusCompleted;
CameraButtons.ShutterKeyPressed += photoCamera_ButtonFullPress;
CameraButtons.ShutterKeyHalfPressed += photoCamera_ButtonHalfPress;
CameraButtons.ShutterKeyReleased += photoCamera_ButtonRelease;
photoCamera.CaptureCompleted += photoCamera_CaptureCompleted;
photoCamera.CaptureImageAvailable += photoCamera_CaptureImageAvailable;
}
קטע קוד זה גורם למבזק לעבוד באופן אוטומטי. בד"כ המצלמה תומכת בכמה רמות של רזולוציה, ככל שהרזולוציה גבוהה יותר כך תגדל כמות הפיקסלים בתמונה הסופית – זה גם אומר שהקובץ הסופי יהיה כבד יותר. קטע הקוד שלנו יעבד את הזנת הווידאו הישירה אך מכיוון שהרזולוציה גבוהה כך גם כמות הפיקסלים שיש לעבד עבור כל פריים. בכדי לצמצם את כמות הפיקסלים וגודל התמונה הסופית הקוד, ראשית, נגדיר רזולוציה מתאימה למצלמה. האובייקט PhotoCamera חושף את AvailableResolutions שהיא תכונה התלויה במרכיבי המצלמה הפיזית המותקנת במכשיר. AvailableResolutions הינו אוסף של אובייקטים מסוג CaptureResolution, אוסף זה ממוין בצורה כזו שהרזולוציה הנמוכה ביותר ממוקמת בתחילתו והגבוהה ביותר בסופו.
כעת נבחן את הגורמים לאירועים הבאים להתרחש:
- AutoFocusCompleted – כאשר מסתיים תהליך Auto-Focus
- ButtonFullPress – לחיצה מלאה על כפתור הצילום
- ButtonHalfPress – לחיצה חלקית על כפתור הצילום
- ButtonRelease – שחרור כפתור הצילום
- CaptureCompleted – בסוף פעולת הלכידה
- CaptureImageAvailable – בסוף פעולת הלכידה כאשר התמונה זמינה
הבה נראה כיצד אנחנו משתמשים באירועים האלה בקטע הקוד שלנו.
כשהמשתמש לוחץ חצי לחיצה על כפתור הצילום נרצה להפעיל את מנגנון הפוקוס האוטומטי:
void photoCamera_ButtonHalfPress(object sender, EventArgs e)
{
photoCamera.Focus();
}
כשמסתיים תהליך ה Auto-Focus ומתרחש האירוע AutoFocusCompleted קטע הקוד שלנו יציג תמונה על החלק המרכזי של המסך:
void photoCamera_AutoFocusCompleted(object sender, CameraOperationCompletedEventArgs e)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
imgFocus.Visibility = Visibility.Visible;
});
}
כשהמשתמש ילחץ לחיצה מלאה יתרחש האירוע ButtonFullPress ונרצה להפעיל את תהליך הלכידה:
void photoCamera_ButtonFullPress(object sender, EventArgs e)
{
photoCamera.CaptureImage();
}
הפונקציה CaptureImage תפעיל את תהליך הלכידה האסינכרוני, וכאשר הוא יסתיים יתרחשו האירועים CaptureCompleted ו- CaptureImageAvailable. כשיתרחש CaptureCompleted נרצה להעלים את המסגרת של Auto-Focus:
void photoCamera_CaptureCompleted(object sender, CameraOperationCompletedEventArgs e)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
imgFocus.Visibility = Visibility.Collapsed;
});
}
לאחר CaptureImageAvailable נשמור את התמונה לספריית המדיה של הטלפון:
void photoCamera_CaptureImageAvailable(object sender, ContentReadyEventArgs e)
{
MediaLibrary mediaLibrary = new MediaLibrary();
string fileName = string.Format("{0:yyyy-MM-dd-HH-mm-ss}.jpg", DateTime.Now);
mediaLibrary.SavePicture(fileName, e.ImageStream);
}
התמונה שצולמה שמורה כ- Stream בארגומנטים של האירוע. קטע הקוד שלנו משתמש במחלקה MediaLibrary מתוך XNA, על-מנת להשתמש בה יש להוסיף reference ל- Microsoft.Xna.Framework ולהוסיף את השורה הבאה:
using Microsoft.Xna.Framework.Media;
במידה והמשתמש שחרר את הכפתור לפני שנלכדה תמונה יתרחש האירוע ButtonRelease קטע הקוד הבא יבטל את המסגרת של Auto-Focus במידה והיא מופיעה:
void photoCamera_ButtonRelease(object sender, EventArgs e)
{
photoCamera.CancelFocus();
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
imgFocus.Visibility = Visibility.Collapsed;
});
}
ביציאה מהדף אל תשכחו לבטל רישום לאירועים של PhotoCamera ולשחרר את האובייקט.
בנוסף לאפשרות להירשם לאירועים השונים של המצלמה ניתן גם להתחבר לחומר הגלם שהמצלמה מספקת באופן ישיר. המחלקה PhotoCamera חושפת מספר פונקציות שמאפשרות העתקה של התוכן הנוכחי במסגרת ה- viewfinder לצורכי המשך עיבוד. התוכן הנראה במסגרת ניתן להעתקה כ ARGB, YUV או YCrCB – בקטע הקוד שלנו נשתמש ב- Buffer של ARGB ע"מ לייצר אפקט של נגטיב (תשליל) על התמונה. נגטיב הוא למעשה הפיכת האזורים הכהים לבהירים ולהפך, בנוסף גם הצבעים מתהפכים: אדום הופך לטורקיז, ירוק לוורוד וכחול לצהוב.
עיבוד הפיקסלים נעשה באופן הבא:
int[] pixelData = new int[photoCamera.PreviewResolution.Width * photoCamera.PreviewResolution.Height];
photoCamera.GetPreviewBufferArgb32(pixelData);
int[] target = new int[pixelData.Length];
for (int i = 0; i < pixelData.Length; i++)
{
target[i] = Negate(pixelData[i]);
}
בקטע הקוד הזה יצרנו מערך גדול מספיק בכדי לאכלס את המידע שבא מ- viewfinder וביצענו GetPreviewBufferArgb32 בכדי לאכלס אותו. נשתמש במערך שנוצר בכדי לאתחל מופע של WriteableBitmap ולהציג את האפקט על המסך:
//Copy to WriteableBitmap
target.CopyTo(previewWriteableBitmap.Pixels, 0);
previewWriteableBitmap.Invalidate();
התוצר הוא אפליקציה שמציגה נגטיב של התמונה על המסך בזמן אמת.
מתי להשתמש ב- CaptureSource וב- PhotoCamera?
שתי הגישות משרתות את אותה המטרה – התחברות להזנה של המצלמה והאפשרות לצלם תמונות. בשתי הגישות ניתן ליישם VidoeBrush בכדי לצפות בתוצאות על המסך, מתי נבחר בכל אחת מהן?
CameraSource – נשתמש בגישה זו כבנוסף לצילום תמונות נהיה מעוניינים גם להקליט וידיאו ואודיו, בנוסף, גישה זו מאפשרת שיתוף קוד בין mobile ל- desktop. גישה זו מאפשרת, בנוסף, עיבוד תמונה פר-פריים (ע"י יצירת codec מבוסס VideoSink מתוך System.Windows.Media) – המבוסס על דחיפה של פריימים (codec).
PhotoCamera – מאפשרת שליטה מדוייקת יותר על המצלמה שבמכשיר, היא מאפשרת שליטה על הרזולוציה, רישום לאירועים של כפתורי המצלמה וניהול של המבזק והפוקוס (כמתואר). עיבוד ווידיאו גם הוא עובד באופן מעט שונה – האפליקציה מושכת את הפריימים מהמצלמה לעיבוד.
בהתאם להבדלים אתם יכולים לבחור מי מבין הגישות מתאימה יותר לצרכים שלכם.
תגובות בפייסבוק