<jupyter_start><jupyter_text>3. Select image patches for processingIn this short chapter, we will start working on mosaic composing. Before we start working on the matching algorithm, we have to prepare the processing framework.We will be working on `MosaicImage` class from `mosaic_maker.mosaic.mosaic_image`module.We have two methods to implement there:  - `_crop_image_to_patch_size`, that crops image so its completely divisible  by our patch size - this is a brutal solution that will save us from   border-case headaches  - `_select_patch_for`, that will use a `PatchPicker` class for selecting  match for currently processed square and will print patch on the mosaic   image. `_select_patch_for` prints also some development info.  Displaying development info on images is common practice for debugging andtracking computer vision algorithms behavior. We will displaythe Sobel version of the image and mosaic to see if our algorithm persists edges well. We will also mark current progress by marking processed shapes with border.Take a while for `MosaicImage` implementation analysis. As you will see, we'reusing threads for its main loop. The reason is that we want to be able todisplay current mosaic status during computation-heavyoperations. In order to prevent mosaic computation from blocking interface,we put its logic to separate thread witch is a common practice in the systems with responsive GUI. Let's start with cropping implementation. At this stage, you should beacquainted with the `Numpy` well enough to crop source image, so it's fully divisible by the `PATCH_SIZE`.  Remember to make `select_patch_for` return a new image instead of modifying the original one.<jupyter_code>import cv2
import numpy as np
from matplotlib import pyplot as plt
from mosaic_maker.mosaic.mosaic_image import MosaicImage
from mosaic_maker.mosaic.patch_picker import PatchPicker
from config import PATCH_SIZE, PROJECT_ROOT
image_path = PROJECT_ROOT / 'assets/test-target.jpg'
test_image = cv2.imread(image_path.as_posix())
plt.imshow(cv2.cvtColor(test_image, cv2.COLOR_BGR2RGB))
test_image_copy = test_image.copy()

cropped_image = MosaicImage._crop_image_to_patch_size(test_image_copy, PATCH_SIZE)
print(f'original shape: {test_image_copy.shape}')
print(f'cropped shape: {cropped_image.shape}')
plt.imshow(cv2.cvtColor(cropped_image, cv2.COLOR_BGR2RGB))<jupyter_output>original shape: (500, 900, 3)
cropped shape: (500, 900, 3)<jupyter_text>The next step will be a bit more sophisticated. We want to modify `_select_patch_for` method so it:  - creates patch class instance for provided `(x, y)` position  - feeds it to `PatchPicker` `pick_patch_for` method to select mosaic patch   (currently `pick_patch_for` isn't implemented, so it always returns a random   image from the sources set  - prints corresponding images on `mosaic` and `sobel_mosaic`  - prints corresponding rectangles on `target_image_copy` and   `target_sobel_image_copy`<jupyter_code>test_image_copy = test_image.copy()

patches_path = PROJECT_ROOT / 'assets/indexed-sources/test'
patch_picker = PatchPicker('test', patches_path)
mosaic_image = MosaicImage(test_image_copy, PATCH_SIZE, patch_picker)
mosaic = np.zeros(mosaic_image.target_image.shape, np.uint8)
sobel_mosaic = np.zeros(mosaic_image.target_sobel_image.shape, np.uint8)
_, grid = plt.subplots(2, 2)
grid[0, 0].imshow(cv2.cvtColor(mosaic_image.target_image, cv2.COLOR_BGR2RGB))
grid[0, 1].imshow(mosaic_image.target_sobel_image, cmap='gray')
grid[1, 0].imshow(cv2.cvtColor(mosaic, cv2.COLOR_BGR2RGB))
grid[1, 1].imshow(sobel_mosaic, cmap='gray')
target_image_copy = mosaic_image.target_image.copy()
target_sobel_image_copy = mosaic_image.target_sobel_image.copy()

(height, width) = target_image_copy.shape[:2]
mosaic_image._compose_mosaic_for(width, height, 
                                 mosaic, sobel_mosaic, 
                                 target_image_copy, target_sobel_image_copy)
_, grid = plt.subplots(2, 2, figsize=(20, 10.3))
grid[0, 0].imshow(cv2.cvtColor(target_image_copy, cv2.COLOR_BGR2RGB))
grid[0, 1].imshow(target_sobel_image_copy, cmap='gray')
grid[1, 0].imshow(cv2.cvtColor(mosaic, cv2.COLOR_BGR2RGB))
grid[1, 1].imshow(sobel_mosaic, cmap='gray')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08002832861189801, 'fraction_numerical': 0.014872521246458924, 'mean_word_length': 5.790064102564102, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '373972', 'license': 'MIT', 'n_tokens_mistral': 1417, 'n_tokens_neox': 1309, 'n_words': 419, 'path': 'notebooks/3-select-image-patches.ipynb', 'repo_name': 'pgolab/mosaic-maker-opencv-python', 'size': '4236'}