זיהוי טקסט בתמונות באמצעות ערכת למידת מכונה ב-iOS

אפשר להשתמש ב-ML Kit כדי לזהות טקסט בתמונות. לערכת למידת מכונה יש גם ל-API לשימוש כללי, שמתאים לזיהוי טקסט בתמונות, כמו של שלט רחוב וממשק API שעבר אופטימיזציה לזיהוי הטקסט של מסמכים. ב-API לשימוש כללי יש מודלים במכשיר ומודלים מבוססי-ענן. זיהוי טקסט מסמכים זמין רק כמודל מבוסס-ענן. לצפייה סקירה כללית להשוואה בין בענן ובמכשיר.

לפני שמתחילים

  1. אם עדיין לא הוספתם את Firebase לאפליקציה, צריך לבצע את הפעולות הבאות במדריך לתחילת העבודה.
  2. כוללים את ספריות ML Kit ב-Podfile:
    pod 'Firebase/MLVision', '6.25.0'
    # If using an on-device API:
    pod 'Firebase/MLVisionTextModel', '6.25.0'
    
    אחרי שמתקינים או מעדכנים את קבוצות ה-Pod של הפרויקט, חשוב לפתוח את ה-Xcode באמצעות ה-.xcworkspace שלו.
  3. מייבאים את Firebase לאפליקציה:

    Swift

    import Firebase

    Objective-C

    @import Firebase;
  4. אם אתם רוצים להשתמש במודל מבוסס-הענן, ואתם עדיין לא הפעלתם את ממשקי ה-API מבוססי-הענן בפרויקט, עליכם לעשות זאת עכשיו:

    1. פתיחת ML Kit דף ממשקי ה-API במסוף Firebase.
    2. אם עדיין לא שדרגתם את הפרויקט לתוכנית תמחור ותשלומים של Blaze, לוחצים על כדי לעשות זאת, אפשר לשדרג. (תתבקש לשדרג רק אם הוא לא בתוכנית Blaze.)

      רק בפרויקטים ברמת Blaze אפשר להשתמש בממשקי API מבוססי-Cloud.

    3. אם ממשקי API מבוססי-ענן עדיין לא מופעלים, לוחצים על הפעלת ממשקי API מבוססי-ענן. ממשקי API.

    אם רוצים להשתמש רק במודל ששמור במכשיר, אפשר לדלג על השלב הזה.

עכשיו אתם מוכנים להתחיל לזהות טקסט בתמונות.

הנחיות להוספת תמונה

  • כדי ש-ML Kit יוכל לזהות טקסט באופן מדויק, תמונות הקלט חייבות להכיל שמיוצג על ידי כמות מספקת של נתוני פיקסלים. באופן אידיאלי, לטקסט לטינית, כל תו צריך להיות בגודל של 16x16 פיקסלים לפחות. בסינית, טקסט ביפנית ובקוריאנית (נתמך רק על ידי ממשקי API מבוססי-ענן), כל אחד צריך להיות בגודל 24x24 פיקסלים. בכל השפות, בדרך כלל אין לשיפור הדיוק של התווים, כך שהם יהיו גדולים מ-24x24 פיקסלים.

    לדוגמה, תמונה בגודל 640x480 יכולה להתאים לסריקה של כרטיס ביקור שממלא את כל רוחב התמונה. כדי לסרוק מסמך שמודפס על נייר בגודל Letter, יכול להיות שתצטרכו תמונה בגודל 720x1280 פיקסלים.

  • מיקוד לא טוב של תמונה עלול לפגוע בדיוק זיהוי הטקסט. אם אתם לא כדי לקבל תוצאות מקובלות, נסו לבקש מהמשתמש לצלם מחדש את התמונה.

  • אם אתה מזהה טקסט באפליקציה בזמן אמת, יכול להיות שגם אנחנו רוצים לקחת בחשבון את המידות הכוללות של תמונות הקלט. קל יותר לעבד תמונות קטנות יותר, ולכן כדי לקצר את זמן האחזור, כדאי לצלם תמונות ברזולוציות נמוכות יותר (תוך שמירה על דרישות הדיוק שצוינו למעלה) ולוודא שהטקסט תופס כמה שיותר מהתמונה. ראו גם טיפים לשיפור הביצועים בזמן אמת.


זיהוי טקסט בתמונות

כדי לזהות טקסט בתמונה באמצעות מודל במכשיר או מודל מבוסס-ענן, הריצו את מזהה הטקסט כמו שמתואר בהמשך.

1. הרצה של מזהה הטקסט

מעבירים את התמונה כ-'UIImage' או כ-'CMSampleBufferRef' אל 'תהליך(_:השלמה:) של 'VisionTextRecognizer' method:
  1. כדי לקבל מופע של VisionTextRecognizer, צריך להתקשר onDeviceTextRecognizer או cloudTextRecognizer:

    Swift

    כדי להשתמש בדגם שבמכשיר:

    let vision = Vision.vision()
    let textRecognizer = vision.onDeviceTextRecognizer()

    כדי להשתמש במודל הענן:

    let vision = Vision.vision()
    let textRecognizer = vision.cloudTextRecognizer()
    
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    let options = VisionCloudTextRecognizerOptions()
    options.languageHints = ["en", "hi"]
    let textRecognizer = vision.cloudTextRecognizer(options: options)

    Objective-C

    כדי להשתמש בדגם שבמכשיר:

    FIRVision *vision = [FIRVision vision];
    FIRVisionTextRecognizer *textRecognizer = [vision onDeviceTextRecognizer];

    כדי להשתמש במודל הענן:

    FIRVision *vision = [FIRVision vision];
    FIRVisionTextRecognizer *textRecognizer = [vision cloudTextRecognizer];
    
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    FIRVisionCloudTextRecognizerOptions *options =
            [[FIRVisionCloudTextRecognizerOptions alloc] init];
    options.languageHints = @[@"en", @"hi"];
    FIRVisionTextRecognizer *textRecognizer = [vision cloudTextRecognizerWithOptions:options];
  2. יצירת אובייקט VisionImage באמצעות UIImage או CMSampleBufferRef

    כדי להשתמש ב-UIImage:

    1. במקרה הצורך, מסובבים את התמונה כך ש-imageOrientation הוא .up.
    2. יצירת אובייקט VisionImage באמצעות סיבוב נכון UIImage. אין לציין מטא-נתונים של סיבוב – צריך להשתמש בערך ברירת המחדל, .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

    כדי להשתמש ב-CMSampleBufferRef:

    1. יוצרים אובייקט VisionImageMetadata שמציין את של נתוני התמונה שכלולים מאגר נתונים זמני של CMSampleBufferRef.

      כדי לקבל את הכיוון של התמונה:

      Swift

      func imageOrientation(
          deviceOrientation: UIDeviceOrientation,
          cameraPosition: AVCaptureDevice.Position
          ) -> VisionDetectorImageOrientation {
          switch deviceOrientation {
          case .portrait:
              return cameraPosition == .front ? .leftTop : .rightTop
          case .landscapeLeft:
              return cameraPosition == .front ? .bottomLeft : .topLeft
          case .portraitUpsideDown:
              return cameraPosition == .front ? .rightBottom : .leftBottom
          case .landscapeRight:
              return cameraPosition == .front ? .topRight : .bottomRight
          case .faceDown, .faceUp, .unknown:
              return .leftTop
          }
      }

      Objective-C

      - (FIRVisionDetectorImageOrientation)
          imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                                 cameraPosition:(AVCaptureDevicePosition)cameraPosition {
        switch (deviceOrientation) {
          case UIDeviceOrientationPortrait:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationLeftTop;
            } else {
              return FIRVisionDetectorImageOrientationRightTop;
            }
          case UIDeviceOrientationLandscapeLeft:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationBottomLeft;
            } else {
              return FIRVisionDetectorImageOrientationTopLeft;
            }
          case UIDeviceOrientationPortraitUpsideDown:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationRightBottom;
            } else {
              return FIRVisionDetectorImageOrientationLeftBottom;
            }
          case UIDeviceOrientationLandscapeRight:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationTopRight;
            } else {
              return FIRVisionDetectorImageOrientationBottomRight;
            }
          default:
            return FIRVisionDetectorImageOrientationTopLeft;
        }
      }

      לאחר מכן, יוצרים את אובייקט המטא-נתונים:

      Swift

      let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
      let metadata = VisionImageMetadata()
      metadata.orientation = imageOrientation(
          deviceOrientation: UIDevice.current.orientation,
          cameraPosition: cameraPosition
      )

      Objective-C

      FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
      AVCaptureDevicePosition cameraPosition =
          AVCaptureDevicePositionBack;  // Set to the capture device you used.
      metadata.orientation =
          [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                       cameraPosition:cameraPosition];
    2. יוצרים אובייקט VisionImage באמצעות אובייקט CMSampleBufferRef והמטא-נתונים של הסבב:

      Swift

      let image = VisionImage(buffer: sampleBuffer)
      image.metadata = metadata

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. לאחר מכן, מעבירים את התמונה ל-method process(_:completion:):

    Swift

    textRecognizer.process(visionImage) { result, error in
      guard error == nil, let result = result else {
        // ...
        return
      }
    
      // Recognized text
    }

    Objective-C

    [textRecognizer processImage:image
                      completion:^(FIRVisionText *_Nullable result,
                                   NSError *_Nullable error) {
      if (error != nil || result == nil) {
        // ...
        return;
      }
    
      // Recognized text
    }];

2. חילוץ טקסט מבלוקים של טקסט מזוהה

אם פעולת זיהוי הטקסט תצליח, היא תחזיר אובייקט ['VisionText'][VisionText]. אובייקט 'VisionText' מכיל את הטקסט המלא מזוהה בתמונה ואפס או יותר [`VisionTextBlock`][VisionTextBlock] אובייקטים. כל 'VisionTextBlock' מייצג קטע טקסט מלבני, שמכיל אפס או יותר אובייקטים מסוג [`VisionTextLine`][VisionTextLine]. כל 'VisionTextLine' האובייקט מכיל אפס אובייקטים של [`VisionTextElement`][VisionTextElement], או יותר שמייצגים מילים וישויות דמויות מילים (תאריכים, מספרים וכן הלאה). לכל אובייקט של 'VisionTextBlock', 'VisionTextLine' ו-'VisionTextElement', אפשר לקבל את הטקסט שמזוהה באזור ואת הקואורדינטות התוחמות האזור. לדוגמה:

Swift

let resultText = result.text
for block in result.blocks {
    let blockText = block.text
    let blockConfidence = block.confidence
    let blockLanguages = block.recognizedLanguages
    let blockCornerPoints = block.cornerPoints
    let blockFrame = block.frame
    for line in block.lines {
        let lineText = line.text
        let lineConfidence = line.confidence
        let lineLanguages = line.recognizedLanguages
        let lineCornerPoints = line.cornerPoints
        let lineFrame = line.frame
        for element in line.elements {
            let elementText = element.text
            let elementConfidence = element.confidence
            let elementLanguages = element.recognizedLanguages
            let elementCornerPoints = element.cornerPoints
            let elementFrame = element.frame
        }
    }
}

Objective-C

NSString *resultText = result.text;
for (FIRVisionTextBlock *block in result.blocks) {
  NSString *blockText = block.text;
  NSNumber *blockConfidence = block.confidence;
  NSArray<FIRVisionTextRecognizedLanguage *> *blockLanguages = block.recognizedLanguages;
  NSArray<NSValue *> *blockCornerPoints = block.cornerPoints;
  CGRect blockFrame = block.frame;
  for (FIRVisionTextLine *line in block.lines) {
    NSString *lineText = line.text;
    NSNumber *lineConfidence = line.confidence;
    NSArray<FIRVisionTextRecognizedLanguage *> *lineLanguages = line.recognizedLanguages;
    NSArray<NSValue *> *lineCornerPoints = line.cornerPoints;
    CGRect lineFrame = line.frame;
    for (FIRVisionTextElement *element in line.elements) {
      NSString *elementText = element.text;
      NSNumber *elementConfidence = element.confidence;
      NSArray<FIRVisionTextRecognizedLanguage *> *elementLanguages = element.recognizedLanguages;
      NSArray<NSValue *> *elementCornerPoints = element.cornerPoints;
      CGRect elementFrame = element.frame;
    }
  }
}

טיפים לשיפור הביצועים בזמן אמת

אם רוצים להשתמש במודל במכשיר כדי לזהות טקסט בזמן אמת צריך לפעול בהתאם להנחיות האלה כדי להשיג את קצב הפריימים הטוב ביותר:

  • ויסות נתונים (throttle) קריאות לכלי לזיהוי הטקסט. אם פריים חדש בסרטון הופך בזמן שמזהה הטקסט פועל, משחררים את המסגרת.
  • אם אתם משתמשים בפלט של מזהה הטקסט כדי ליצור שכבת-על של גרפיקה מקבלים קודם את התוצאה מ-ML Kit ואז מעבדים את התמונה וליצור שכבת-על בשלב אחד. כך תוכלו להציג את משטח המסך פעם אחת בלבד לכל מסגרת קלט. לדוגמה, תוכלו לעיין בכיתות previewOverlayView ו-FIRDetectionOverlayView באפליקציית הדוגמה הזו.
  • כדאי לצלם תמונות ברזולוציה נמוכה יותר. עם זאת, חשוב גם לזכור בדרישות של מידות התמונה ב-API הזה.

השלבים הבאים


זיהוי טקסט בתמונות של מסמכים

כדי לזהות טקסט של מסמך, צריך להגדיר ולהפעיל מודל מבוסס-ענן מזהה טקסט של מסמכים, כמו שמתואר בהמשך.

ממשק ה-API לזיהוי טקסט במסמכים, המתואר בהמשך, מספק ממשק אמור להיות נוח יותר לעבודה עם תמונות של מסמכים. אבל, לפעמים אם אתם מעדיפים את הממשק שמסופק על ידי ה-sparse text API, תוכלו להשתמש בו במקום לסרוק מסמכים על ידי הגדרת מזהה הטקסט בענן להשתמש במודל הטקסט הדחיסה.

כדי להשתמש בממשק ה-API לזיהוי טקסט במסמך:

1. הרצת הכלי לזיהוי טקסט

מעבירים את התמונה כ-UIImage או כ-CMSampleBufferRef אל process(_:completion:) של VisionDocumentTextRecognizer method:

  1. כדי לקבל מופע של VisionDocumentTextRecognizer, צריך להתקשר cloudDocumentTextRecognizer:

    Swift

    let vision = Vision.vision()
    let textRecognizer = vision.cloudDocumentTextRecognizer()
    
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    let options = VisionCloudDocumentTextRecognizerOptions()
    options.languageHints = ["en", "hi"]
    let textRecognizer = vision.cloudDocumentTextRecognizer(options: options)

    Objective-C

    FIRVision *vision = [FIRVision vision];
    FIRVisionDocumentTextRecognizer *textRecognizer = [vision cloudDocumentTextRecognizer];
    
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    FIRVisionCloudDocumentTextRecognizerOptions *options =
            [[FIRVisionCloudDocumentTextRecognizerOptions alloc] init];
    options.languageHints = @[@"en", @"hi"];
    FIRVisionDocumentTextRecognizer *textRecognizer = [vision cloudDocumentTextRecognizerWithOptions:options];
  2. יוצרים אובייקט VisionImage באמצעות UIImage או CMSampleBufferRef.

    כדי להשתמש ב-UIImage:

    1. במקרה הצורך, מסובבים את התמונה כך ש-imageOrientation הוא .up.
    2. יצירת אובייקט VisionImage באמצעות סיבוב נכון UIImage. אין לציין מטא-נתונים של סיבוב – צריך להשתמש בערך ברירת המחדל, .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

    כדי להשתמש ב-CMSampleBufferRef:

    1. יוצרים אובייקט VisionImageMetadata שמציין את של נתוני התמונה שכלולים מאגר נתונים זמני של CMSampleBufferRef.

      כדי לקבל את הכיוון של התמונה:

      Swift

      func imageOrientation(
          deviceOrientation: UIDeviceOrientation,
          cameraPosition: AVCaptureDevice.Position
          ) -> VisionDetectorImageOrientation {
          switch deviceOrientation {
          case .portrait:
              return cameraPosition == .front ? .leftTop : .rightTop
          case .landscapeLeft:
              return cameraPosition == .front ? .bottomLeft : .topLeft
          case .portraitUpsideDown:
              return cameraPosition == .front ? .rightBottom : .leftBottom
          case .landscapeRight:
              return cameraPosition == .front ? .topRight : .bottomRight
          case .faceDown, .faceUp, .unknown:
              return .leftTop
          }
      }

      Objective-C

      - (FIRVisionDetectorImageOrientation)
          imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                                 cameraPosition:(AVCaptureDevicePosition)cameraPosition {
        switch (deviceOrientation) {
          case UIDeviceOrientationPortrait:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationLeftTop;
            } else {
              return FIRVisionDetectorImageOrientationRightTop;
            }
          case UIDeviceOrientationLandscapeLeft:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationBottomLeft;
            } else {
              return FIRVisionDetectorImageOrientationTopLeft;
            }
          case UIDeviceOrientationPortraitUpsideDown:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationRightBottom;
            } else {
              return FIRVisionDetectorImageOrientationLeftBottom;
            }
          case UIDeviceOrientationLandscapeRight:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationTopRight;
            } else {
              return FIRVisionDetectorImageOrientationBottomRight;
            }
          default:
            return FIRVisionDetectorImageOrientationTopLeft;
        }
      }

      לאחר מכן, יוצרים את אובייקט המטא-נתונים:

      Swift

      let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
      let metadata = VisionImageMetadata()
      metadata.orientation = imageOrientation(
          deviceOrientation: UIDevice.current.orientation,
          cameraPosition: cameraPosition
      )

      Objective-C

      FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
      AVCaptureDevicePosition cameraPosition =
          AVCaptureDevicePositionBack;  // Set to the capture device you used.
      metadata.orientation =
          [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                       cameraPosition:cameraPosition];
    2. יוצרים אובייקט VisionImage באמצעות אובייקט CMSampleBufferRef והמטא-נתונים של הסבב:

      Swift

      let image = VisionImage(buffer: sampleBuffer)
      image.metadata = metadata

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. לאחר מכן, מעבירים את התמונה ל-method process(_:completion:):

    Swift

    textRecognizer.process(visionImage) { result, error in
      guard error == nil, let result = result else {
        // ...
        return
      }
    
      // Recognized text
    }

    Objective-C

    [textRecognizer processImage:image
                      completion:^(FIRVisionDocumentText *_Nullable result,
                                   NSError *_Nullable error) {
      if (error != nil || result == nil) {
        // ...
        return;
      }
    
        // Recognized text
    }];

2. חילוץ טקסט מבלוקים של טקסט מזוהה

אם פעולת זיהוי הטקסט תצליח, היא תחזיר אובייקט VisionDocumentText. אובייקט VisionDocumentText מכיל את הטקסט המלא שזוהה בתמונה ואת היררכיית האובייקטים שמשקפת את המבנה של המסמך שזוהה:

לכל VisionDocumentTextBlock, VisionDocumentTextParagraph, VisionDocumentTextWord, ואובייקט VisionDocumentTextSymbol, אפשר לקבל את הטקסט שמזוהה באזור ואת הקואורדינטות התוחמות של האזור.

לדוגמה:

Swift

let resultText = result.text
for block in result.blocks {
    let blockText = block.text
    let blockConfidence = block.confidence
    let blockRecognizedLanguages = block.recognizedLanguages
    let blockBreak = block.recognizedBreak
    let blockCornerPoints = block.cornerPoints
    let blockFrame = block.frame
    for paragraph in block.paragraphs {
        let paragraphText = paragraph.text
        let paragraphConfidence = paragraph.confidence
        let paragraphRecognizedLanguages = paragraph.recognizedLanguages
        let paragraphBreak = paragraph.recognizedBreak
        let paragraphCornerPoints = paragraph.cornerPoints
        let paragraphFrame = paragraph.frame
        for word in paragraph.words {
            let wordText = word.text
            let wordConfidence = word.confidence
            let wordRecognizedLanguages = word.recognizedLanguages
            let wordBreak = word.recognizedBreak
            let wordCornerPoints = word.cornerPoints
            let wordFrame = word.frame
            for symbol in word.symbols {
                let symbolText = symbol.text
                let symbolConfidence = symbol.confidence
                let symbolRecognizedLanguages = symbol.recognizedLanguages
                let symbolBreak = symbol.recognizedBreak
                let symbolCornerPoints = symbol.cornerPoints
                let symbolFrame = symbol.frame
            }
        }
    }
}

Objective-C

NSString *resultText = result.text;
for (FIRVisionDocumentTextBlock *block in result.blocks) {
  NSString *blockText = block.text;
  NSNumber *blockConfidence = block.confidence;
  NSArray<FIRVisionTextRecognizedLanguage *> *blockRecognizedLanguages = block.recognizedLanguages;
  FIRVisionTextRecognizedBreak *blockBreak = block.recognizedBreak;
  CGRect blockFrame = block.frame;
  for (FIRVisionDocumentTextParagraph *paragraph in block.paragraphs) {
    NSString *paragraphText = paragraph.text;
    NSNumber *paragraphConfidence = paragraph.confidence;
    NSArray<FIRVisionTextRecognizedLanguage *> *paragraphRecognizedLanguages = paragraph.recognizedLanguages;
    FIRVisionTextRecognizedBreak *paragraphBreak = paragraph.recognizedBreak;
    CGRect paragraphFrame = paragraph.frame;
    for (FIRVisionDocumentTextWord *word in paragraph.words) {
      NSString *wordText = word.text;
      NSNumber *wordConfidence = word.confidence;
      NSArray<FIRVisionTextRecognizedLanguage *> *wordRecognizedLanguages = word.recognizedLanguages;
      FIRVisionTextRecognizedBreak *wordBreak = word.recognizedBreak;
      CGRect wordFrame = word.frame;
      for (FIRVisionDocumentTextSymbol *symbol in word.symbols) {
        NSString *symbolText = symbol.text;
        NSNumber *symbolConfidence = symbol.confidence;
        NSArray<FIRVisionTextRecognizedLanguage *> *symbolRecognizedLanguages = symbol.recognizedLanguages;
        FIRVisionTextRecognizedBreak *symbolBreak = symbol.recognizedBreak;
        CGRect symbolFrame = symbol.frame;
      }
    }
  }
}

השלבים הבאים