diff --git a/software/SAT0_OBC/SatImageTaking/earth.py b/software/SAT0_OBC/SatImageTaking/earth.py index 49dbb01..7f966e6 100644 --- a/software/SAT0_OBC/SatImageTaking/earth.py +++ b/software/SAT0_OBC/SatImageTaking/earth.py @@ -17,21 +17,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ''' -from cv2 import cvtColor as cv2_cvtColor -from cv2 import morphologyEx as cv2_morphologyEx -from cv2 import GaussianBlur as cv2_GaussianBlur -from cv2 import threshold as cv2_threshold -from cv2 import adaptiveThreshold as cv2_adaptiveThreshold -from cv2 import bitwise_and as cv2_bitwise_and -from cv2 import bitwise_not as cv2_bitwise_not -from cv2 import Sobel as cv2_Sobel -from cv2 import circle as cv2_circle -from cv2 import findContours as cv2_findContours -from cv2 import dilate as cv2_dilate -from cv2 import minEnclosingCircle as cv2_minEnclosingCircle -from cv2 import THRESH_TOZERO, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV, MORPH_CLOSE, MORPH_OPEN, RETR_EXTERNAL,\ - CHAIN_APPROX_SIMPLE, COLOR_BGR2GRAY +# 17.1.2024 asaf h: changed cv2 import cv2_ >> cv2. +import cv2 import numpy as np from SatImageTaking import utils @@ -63,7 +51,7 @@ def __init__(self, path_or_image: str or np.ndarray, dim=None) -> None: """Constructor for earth class. Initializes the attributes of the class""" self.mask = None self.image = utils.get_image(path_or_image, dim=dim) - self.gray_image = cv2_cvtColor(self.image, COLOR_BGR2GRAY) + self.gray_image = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY) self.clear_sky, self.clear_earth, self.earth_mask = self.get_mask() self.is_earth = self._is_earth() self.earth_percent = self._earth_percent() @@ -80,21 +68,21 @@ def _is_earth_at_night(self): Returns: bool: True if the image contain Earth at night, False otherwise. """ - gray_image = cv2_GaussianBlur( + gray_image = cv2.GaussianBlur( self.gray_image, (3, 3), 0) - _, gray_image = cv2_threshold( - self.gray_image, 80, 255, THRESH_TOZERO) - mask = cv2_adaptiveThreshold( - gray_image, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV, blockSize=3, C=5) + _, gray_image = cv2.threshold( + self.gray_image, 80, 255, cv2.THRESH_TOZERO) + mask = cv2.adaptiveThreshold( + gray_image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, blockSize=3, C=5) kernel = np.ones((5, 5), np.uint8) - mask = cv2_morphologyEx(mask, MORPH_CLOSE, kernel) + mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) - contours, _ = cv2_findContours( - mask, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE)[-2:] + contours, _ = cv2.findContours( + mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2:] self.mask = mask for cnt in contours: - (x, y), r = cv2_minEnclosingCircle(cnt) + (x, y), r = cv2.minEnclosingCircle(cnt) if r > 60: return True return False @@ -104,17 +92,17 @@ def get_curve(self): Returns: np.ndarray: image that contains the curve o the Earth""" - gauss = cv2_GaussianBlur(self.gray_image, (7, 7), 0) + gauss = cv2.GaussianBlur(self.gray_image, (7, 7), 0) - _, gauss = cv2_threshold(gauss, 80, 255, THRESH_TOZERO) + _, gauss = cv2.threshold(gauss, 80, 255, cv2.THRESH_TOZERO) - adaptive_mean = cv2_adaptiveThreshold(gauss, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV, + adaptive_mean = cv2.adaptiveThreshold(gauss, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, blockSize=201, C=3) kernel = np.ones((5, 5), np.uint8) - adaptive_mean = cv2_morphologyEx( - adaptive_mean, MORPH_CLOSE, kernel) - adaptive_mean = cv2_morphologyEx( - adaptive_mean, MORPH_OPEN, kernel) + adaptive_mean = cv2.morphologyEx( + adaptive_mean, cv2.MORPH_CLOSE, kernel) + adaptive_mean = cv2.morphologyEx( + adaptive_mean, cv2.MORPH_OPEN, kernel) return adaptive_mean def _is_earth(self): @@ -142,28 +130,28 @@ def _earth_percent(self): def _get_earth_mask(self): """Returns the earth mask in the image""" - gauss = cv2_GaussianBlur(self.gray_image, (5, 5), 0) + gauss = cv2.GaussianBlur(self.gray_image, (5, 5), 0) - _, gray_image = cv2_threshold( - gauss, 30, 255, THRESH_TOZERO) + _, gray_image = cv2.threshold( + gauss, 30, 255, cv2.THRESH_TOZERO) - thresh = cv2_Sobel(src=gray_image, ddepth=-1, dx=1, dy=1, ksize=11) + thresh = cv2.Sobel(src=gray_image, ddepth=-1, dx=1, dy=1, ksize=11) kernel = np.ones((10, 10), np.uint8) - thresh = cv2_morphologyEx(thresh, MORPH_CLOSE, kernel) + thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) kernel = np.ones((20, 20), np.uint8) - thresh = cv2_morphologyEx(thresh, MORPH_OPEN, kernel) + thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel) kernel = np.ones((40, 40), np.uint8) - thresh = cv2_morphologyEx(thresh, MORPH_CLOSE, kernel) + thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) kernel = np.ones((70, 70), np.uint8) - thresh = cv2_morphologyEx(thresh, MORPH_OPEN, kernel) + thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel) return thresh def draw_black_cir(self, contours): """This function helps to eliminate all the stars in the sky or fill the holes in Earth mask""" for center, radius in contours: - cv2_circle(self.mask, center, radius, (0, 0, 0), -1) + cv2.circle(self.mask, center, radius, (0, 0, 0), -1) def _get_cir_contours(self): """ @@ -173,11 +161,11 @@ def _get_cir_contours(self): list: A list containing the contours of the circles in the image """ height, width = self.gray_image.shape - contours, _ = cv2_findContours( - self.mask, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE)[-2:] + contours, _ = cv2.findContours( + self.mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2:] all_cir = [] for cnt in contours: - (x, y), r = cv2_minEnclosingCircle(cnt) + (x, y), r = cv2.minEnclosingCircle(cnt) if r < int(max(height, width) * 0.3): x, y, r = int(x), int(y), int(r) center = x, y @@ -186,10 +174,10 @@ def _get_cir_contours(self): def fill_earth(self): """Fill all the little holes in Earth mask image.""" - self.mask = cv2_bitwise_not(self.mask) + self.mask = cv2.bitwise_not(self.mask) contours = self._get_cir_contours() self.draw_black_cir(contours) - self.mask = cv2_bitwise_not(self.mask) + self.mask = cv2.bitwise_not(self.mask) def get_mask(self): """ @@ -203,18 +191,18 @@ def get_mask(self): self.draw_black_cir(contours) kernel = np.ones((5, 5), np.uint8) - self.mask = cv2_dilate(self.mask, kernel, iterations=2) + self.mask = cv2.dilate(self.mask, kernel, iterations=2) self.fill_earth() kernel = np.ones((15, 15), np.uint8) - self.mask = cv2_dilate(self.mask, kernel, iterations=1) + self.mask = cv2.dilate(self.mask, kernel, iterations=1) - clear_earth = cv2_bitwise_and( + clear_earth = cv2.bitwise_and( self.image, self.image, mask=self.mask) - mask = cv2_bitwise_not(self.mask) - clear_sky = cv2_bitwise_and( + mask = cv2.bitwise_not(self.mask) + clear_sky = cv2.bitwise_and( self.image, self.image, mask=mask) return clear_sky, clear_earth, self.mask diff --git a/software/SAT0_OBC/SatImageTaking/star_finder.py b/software/SAT0_OBC/SatImageTaking/star_finder.py index 8b53065..4a6f88a 100644 --- a/software/SAT0_OBC/SatImageTaking/star_finder.py +++ b/software/SAT0_OBC/SatImageTaking/star_finder.py @@ -1,4 +1,4 @@ -''' +""" @file star_finder.py @brief A file for the finding stars function. @@ -16,22 +16,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - ''' -from cv2 import cvtColor as cv2_cvtColor -from cv2 import morphologyEx as cv2_morphologyEx -from cv2 import GaussianBlur as cv2_GaussianBlur -from cv2 import threshold as cv2_threshold -from cv2 import adaptiveThreshold as cv2_adaptiveThreshold -from cv2 import bitwise_and as cv2_bitwise_and -from cv2 import bitwise_not as cv2_bitwise_not -from cv2 import Sobel as cv2_Sobel -from cv2 import circle as cv2_circle -from cv2 import findContours as cv2_findContours -from cv2 import dilate as cv2_dilate -from cv2 import minEnclosingCircle as cv2_minEnclosingCircle -from cv2 import THRESH_TOZERO, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV, MORPH_CLOSE, MORPH_OPEN, RETR_EXTERNAL,\ - CHAIN_APPROX_SIMPLE, COLOR_BGR2GRAY, THRESH_BINARY +""" +# 17.1.2024 asaf h: changed cv2 import cv2_ >> cv2. +import cv2 from copy import deepcopy # import matplotlib.pyplot as plt import numpy as np @@ -55,18 +43,18 @@ class star_finder: gray_image (np.ndarray): The grayscale version of the original image. sensitivity (int): Sensitivity of the threshold used when finding stars. mask (np.ndarray): The mask used to find stars. - stars (list): A list of detected stars, including center, radius, and brightness. + stars (list): A list of detected stars, including thier image, center(x,y), radius, and brightness. N_stars (int): The number of stars to detect. Methods: find_stars(): Finds stars in the image and stores them in the 'stars' attribute. extract_star(): Extracts a single star from the image. get_threshold(): Returns the threshold used to find stars. - get_brightness(): Returns the brightness of a star. draw (): draw the stars on the image. """ - def __init__(self, path_or_image: str or list, gray_image=0, sensitivity=100, N_stars=None, dim=None, draw=False,) -> None: + # 19.9.2023 asaf h: lowered the default sensitivity for better result + def __init__(self, path_or_image: str or list, gray_image=0, sensitivity=50, N_stars=None, dim=None, draw=False,) -> None: """ Initializes the star_finder object. """ @@ -75,7 +63,7 @@ def __init__(self, path_or_image: str or list, gray_image=0, sensitivity=100, N_ if isinstance(gray_image, np.ndarray): self.gray_image = gray_image else: - self.gray_image = cv2_cvtColor(self.image, COLOR_BGR2GRAY) + self.gray_image = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY) self.sensitivity = sensitivity self.mask = self.get_threshold() @@ -99,22 +87,29 @@ def find_stars(self): r - the radius of the star. brightness- the brightness of the star. """ - contours, _ = cv2_findContours( - self.mask, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE)[-2:] + contours, _ = cv2.findContours( + self.mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2:] stars_data = [] for cnt in contours: - (x, y), r = cv2_minEnclosingCircle(cnt) + (x, y), r = cv2.minEnclosingCircle(cnt) if r > 10: continue x, y, r = int(x), int(y), int(r) center = x, y if r == 0: r = 1 + r += 1 star = self.extract_star(y, x, r) - stars_data.append( - (star, center, r, star_finder.get_brightness(star))) + # 19.9.2023 asaf h: removed my get_brightness function for: brightest_pixal = np.amax(star) + brightest_pixal = np.amax(star) + # 19.9.2023 asaf h: filter out all the stars that dont not meet the minimum thresholdׄ + if brightest_pixal < self.sensitivity: + continue - stars_data.sort(key=lambda cnt: cnt[2], reverse=True) + stars_data.append( + (star, center, r, brightest_pixal)) + # 19.9.2023 asaf h: sort by brightest_pixal + stars_data.sort(key=lambda cnt: cnt[3], reverse=True) return stars_data def extract_star(self, y, x, r): @@ -131,9 +126,9 @@ def extract_star(self, y, x, r): """ star = self.gray_image[max(y - r, 0): y + r, max(x - r, 0): x + r] mask = np.zeros_like(self.gray_image) - mask = cv2_circle(mask, (x, y), r, (255, 255, 255), -1) + mask = cv2.circle(mask, (x, y), r, (255, 255, 255), -1) mask = mask[max(y - r, 0): y + r, max(x - r, 0): x + r] - cropped_star = cv2_bitwise_and(star, star, mask=mask) + cropped_star = cv2.bitwise_and(star, star, mask=mask) return cropped_star def get_threshold(self): @@ -143,37 +138,21 @@ def get_threshold(self): Returns: np.ndarray: Thresholded image. """ - gray_image = cv2_GaussianBlur( + gray_image = cv2.GaussianBlur( self.gray_image, (5, 5), 0) - _, gray_image = cv2_threshold( - self.gray_image, self.sensitivity, 255, THRESH_TOZERO) - adaptive_mean = cv2_adaptiveThreshold( - gray_image, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV, blockSize=5, C=1) + _, gray_image = cv2.threshold( + self.gray_image, self.sensitivity, 255, cv2.THRESH_TOZERO) + adaptive_mean = cv2.adaptiveThreshold( + gray_image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, blockSize=5, C=1) return adaptive_mean - @staticmethod - def get_brightness(star): - """ - Calculates the brightness of a star. - - Args: - star (np.ndarray): Cropped star. - - Returns: - int: Brightness of the star. - """ - brightest_pixal = np.amax(star) - p = 0.1 - threshold_pixal = brightest_pixal - brightest_pixal * p * 3 - return np.sum(star[star > threshold_pixal]) - def draw(self): """ Draws the stars on the image. """ self.draw_image = deepcopy(self.image) for star, center, radius, b in self.stars[:self.N_stars]: - cv2_circle(self.draw_image, center, radius + 4, (0, 255, 0), 2) + cv2.circle(self.draw_image, center, radius + 4, (0, 255, 0), 2) def get_data(self): """ diff --git a/software/SAT0_OBC/SatImageTaking/start_service.py b/software/SAT0_OBC/SatImageTaking/start_service.py index b4c9069..935abe0 100644 --- a/software/SAT0_OBC/SatImageTaking/start_service.py +++ b/software/SAT0_OBC/SatImageTaking/start_service.py @@ -17,13 +17,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ''' -from cv2 import imwrite as cv2_imwrite -from cv2 import imread as cv2_imread -from cv2 import cvtColor as cv2_cvtColor -from cv2 import COLOR_BGR2GRAY, cvtColor, IMWRITE_JPEG_QUALITY -from cv2 import INTER_AREA as cv2_INTER_AREA -from cv2 import getGaussianKernel as cv2_getGaussianKernel -from cv2 import resize as cv2_resize + +# 17.1.2024 asaf h: changed cv2 import cv2_ >> cv2. +import cv2 import numpy as np import os from SatImageTaking import earth, star_finder, utils, LIT @@ -54,7 +50,7 @@ def main(outputFolder: str, parameters_list: list) -> None: 3: makeIcon (img_path, width, height, quality_factor, gray) will create an icon out of a given image path with custom parameters. - 4: StarAnalysisOnPhotosThatAlreadyBeTaken (mission_count, width, height, sensitive, n_stars, with_mask) will run star analysis + 4: StarAnalysis (mission_count, width, height, sensitive, n_stars, with_mask) will run star analysis on photos that have already been taken with given a custom parameters. 5: testingOnPreSavedImages (missionID, x, y, quality_factor, gray, vga, img_n) will be able to run the above functions @@ -149,7 +145,8 @@ def main(outputFolder: str, parameters_list: list) -> None: img_n = parameters_list[9] if len(parameters_list) > 9 else 0 mission = mission_Table[missionType] - mission(x, y, quality_factor, gray, vga, img_n) + # 19.9.2023 asaf h: missionID was missing, it is like missionType, 7 arguments are needed for testing + mission(missionID, x, y, quality_factor, gray, vga, img_n) def TakePhotoStarAnalysis(quality: int, width: int, height: int, Shutter: int, ISO: int) -> None: @@ -167,13 +164,7 @@ def TakePhotoStarAnalysis(quality: int, width: int, height: int, Shutter: int, I None """ print("Started TakePhotoStarAnalysis") - if width == 0: - width = 640 - if height == 0: - height = 480 - if quality == 0: - quality = 10 - + # 19.9.2023 asaf h: there is no need to define the parameters, takePictureWithParameters does that. img = takePictureWithParameters(quality, width, height, Shutter, ISO) # define Dimensions @@ -193,8 +184,8 @@ def TakePhotoStarAnalysis(quality: int, width: int, height: int, Shutter: int, I print("Starting writeMetaDataStars") writeMetaDataStars(stars_list) - -def StarAnalysis(mission_count: int, width=1280, height=720, sensitive=100, n_stars=30, without_earth_mask=0): +# 19.9.2023 asaf h: the default numbers needs to be removed cuz 0 is sent not None, they will be difined later +def StarAnalysis(mission_count: int, width: int, height: int, sensitive: int, n_stars: int, without_earth_mask=0): """Analyzes an image to identify stars Args: @@ -209,12 +200,13 @@ def StarAnalysis(mission_count: int, width=1280, height=720, sensitive=100, n_st stars_list (list): A list of the detected stars in the image """ print("Started StarAnalysis") + # 19.9.2023 asaf h: fixed the default numbers if width == 0: - width = 640 + width = 1280 if height == 0: - height = 480 + height = 720 if sensitive == 0: - sensitive = 100 + sensitive = 50 if n_stars == 0: n_stars = 30 @@ -227,7 +219,8 @@ def StarAnalysis(mission_count: int, width=1280, height=720, sensitive=100, n_st # star detection detected_object = findStarsNoDarkEarth(img, img_object, sensitive, n_stars) - detected_object.draw() + # 19.9.2023 asaf h: detected_object.draw() and saveFinalImg(detected_object.draw_image, "Detected") + # will save the image with the green circels on the stars on the pi for future analysis. its unnecessary. # get stars list: stars_list = get_stars(detected_object) @@ -236,34 +229,17 @@ def StarAnalysis(mission_count: int, width=1280, height=720, sensitive=100, n_st print("Starting writeMetaDataStars") writeMetaDataStars(stars_list) print("Starting saveFinalImg") - saveFinalImg(detected_object.draw_image, "Detected") - - -def takeStandardPicture() -> np.ndarray: - """Takes a standard picture with default raspistill image capture parameters. - The picture is taken by a cmd function - - Returns: - img (np.ndarray): A numpy array containing the image - """ - - print("Started takeStandardPicture") - comm = f"raspistill -o {output}/Img.jpeg" - print(comm) - os.system(comm) - read = f"{output}/Img.jpeg" - print(read) - img = cv2_imread(read) - return img +# 19.9.2023 asaf h: removed takeStandardPicture function cuz there is no need to. check line 268 (else) +# 19.9.2023 asaf h: fixed the main function takePictureWithParameters. rony please double check it again. it should be fine def takePictureWithParameters(quality: int, width: int, height: int, Shutter: int, ISO: int) -> np.ndarray: """Take a picture with specified parameters Args: - quality (int): The quality of the image (default 10) - width (int): The width of the image (default 640) - height (int): The height of the image (default 480) + quality (int): The quality of the image (default 100) + width (int): The width of the image (default 1280) + height (int): The height of the image (default 720) Shutter (int): The shutter speed of the camera (default 0) ISO (int): The ISO of the camera (default 0) @@ -273,35 +249,26 @@ def takePictureWithParameters(quality: int, width: int, height: int, Shutter: in print("Started takePictureWithParameters") if quality == 0: - quality = 10 + quality = 100 if width == 0: - width = 640 + width = 1280 if height == 0: - height = 480 + height = 720 - if (quality != 100 and width != 1280 and height != 720 and Shutter != 0 and ISO != 0): + if (Shutter != 0 and ISO != 0): command_with_parameters = f"raspistill -o {output}/Img.jpeg --quality {quality} --width {width} --height {height} --shutter {Shutter} --ISO {ISO} -th none" - os.system(command_with_parameters) - read = f"{output}/Img.jpeg" - img = cv2_imread(read) - elif (quality != 100 and width != 1280 and height != 720 and Shutter != 0): + elif (Shutter != 0): command_with_parameters = f"raspistill -o {output}/Img.jpeg --quality {quality} --width {width} --height {height} --shutter {Shutter} -th none" - os.system(command_with_parameters) - read = f"{output}/Img.jpeg" - img = cv2_imread(read) - elif (quality != 100 and width != 1280 and height != 720): - command_with_parameters = f"raspistill -o {output}/Img.jpeg --quality {quality} --width {width} --height {height} -th none" - os.system(command_with_parameters) - read = f"{output}/Img.jpeg" - img = cv2_imread(read) - elif (quality != 100): - command_with_parameters = f"raspistill -o {output}/Img.jpeg --quality {quality} -th none" - os.system(command_with_parameters) - read = f"{output}/Img.jpeg" - img = cv2_imread(read) + elif (ISO != 0): + command_with_parameters = f"raspistill -o {output}/Img.jpeg --quality {quality} --width {width} --height {height} --ISO {ISO} -th none" else: - img = takeStandardPicture() - read = f"{output}/Img.jpeg" + command_with_parameters = f"raspistill -o {output}/Img.jpeg --quality {quality} --width {width} --height {height} -th none" + + # 19.9.2023 asaf h: removed the take standard picture. unnecessary function + + os.system(command_with_parameters) + read = f"{output}/Img.jpeg" + img = cv2.imread(read) print("Compressing") CompressImage(img, output, save_full=True) @@ -313,8 +280,8 @@ def takePictureWithParameters(quality: int, width: int, height: int, Shutter: in writeMetaDataSmallImg(read, isGoodImage) return img - -def findStarsNoDarkEarth(img: np.ndarray, img_object: np.ndarray, sensitive=100, n_stars=30) -> np.ndarray: +# 19.9.2023 asaf h: lower the default sensitive for better result +def findStarsNoDarkEarth(img: np.ndarray, img_object: np.ndarray, sensitive=50, n_stars=30) -> np.ndarray: """Finds stars in an image while the Earth is lit and not dark Args: @@ -326,7 +293,7 @@ def findStarsNoDarkEarth(img: np.ndarray, img_object: np.ndarray, sensitive=100, Returns: detected_object (np.ndarray): An array containing the detected objects """ - gray_image = cvtColor(img_object.clear_sky, COLOR_BGR2GRAY) + gray_image = cv2.cvtColor(img_object.clear_sky, cv2.COLOR_BGR2GRAY) star_detection = star_finder.star_finder( img, gray_image, sensitivity=sensitive, N_stars=n_stars) return star_detection @@ -431,10 +398,10 @@ def crop_and_compress(mission_count: int, x: int, y: int, quality_factor=95, gra img = utils.get_image(mission_count) crop_img = utils.crop(img, x, y, qvga) if(gray): - crop_img = cv2_cvtColor(crop_img, COLOR_BGR2GRAY) + crop_img = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY) saveFinalImg(crop_img, 'Img', quality_factor) read = f"{output}/Img.jpeg" - cropped_img = cv2_imread(read) + cropped_img = cv2.imread(read) CompressImage(cropped_img, output, save_full=True) makeIcon(crop_img) @@ -449,7 +416,7 @@ def decideIfGoodImage(img: np.ndarray) -> bool: int: 1 if the image is good, 0 otherwise. """ img = utils.get_image(img) - img = cv2_cvtColor(img, COLOR_BGR2GRAY) + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) w, h = img.shape black_percent = np.sum(img < 15)*100/(w*h) return 1 if black_percent > 95 else 0 @@ -478,9 +445,9 @@ def makeIcon(img_path: str, width=80, height=80, quality_factor=21, gray=0) -> N dim = (width, height) img = utils.get_image(img_path) - resized_image = cv2_resize(img, dim, interpolation=cv2_INTER_AREA) + resized_image = cv2.resize(img, dim, interpolation=cv2.INTER_AREA) if gray: - resized_image = cvtColor(resized_image, COLOR_BGR2GRAY) + resized_image = cv2.cvtColor(resized_image, cv2.COLOR_BGR2GRAY) saveFinalImg(resized_image, 'icon', quality_factor) @@ -496,8 +463,8 @@ def saveFinalImg(star_detection: np.ndarray, name: str, compress_factor=95) -> N None. """ FINAL_PATH = f"{output}/{name}.jpeg" - compress = [IMWRITE_JPEG_QUALITY, compress_factor] - cv2_imwrite(FINAL_PATH, star_detection, compress) + compress = [cv2.IMWRITE_JPEG_QUALITY, compress_factor] + cv2.imwrite(FINAL_PATH, star_detection, compress) def testing(missionType: int, x: int, y: int, quality_factor: int, gray: bool, qvga: bool, img_n=0) -> int: @@ -517,7 +484,16 @@ def testing(missionType: int, x: int, y: int, quality_factor: int, gray: bool, q """ img_path = f"SatImageTaking/photos{img_n}.jpeg" mission = mission_Table[missionType] - mission(img_path, x, y, quality_factor, gray, qvga) + # 19.9.2023 asaf h: each function has its own order + if mission == makeIcon: # + mission(img_path, x, y, quality_factor, gray) + elif mission == crop_and_compress: + mission(img_path, x, y, quality_factor, gray, qvga) + else: #StarAnalysis + sensitive = quality_factor + n_stars = gray + without_earth_mask = qvga + mission(img_path, x, y, sensitive, n_stars, without_earth_mask) def CompressImage(in_img: np.ndarray, path: str, max_size: int = 16 * LIT.KBYTE, comp_gray=True, save_full=False) -> str and np.ndarray: @@ -535,9 +511,9 @@ def CompressImage(in_img: np.ndarray, path: str, max_size: int = 16 * LIT.KBYTE, """ img = in_img if len(in_img.shape) > 2 and comp_gray: - img = cv2_cvtColor(img, COLOR_BGR2GRAY) + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img = img / 255 - g_ker = cv2_getGaussianKernel(5, -1) + g_ker = cv2.getGaussianKernel(5, -1) g_ker = g_ker @ g_ker.T lap_pyr = LIT.pyrLap(img, 7, g_ker) jp2_img = img[:, :, [2, 1, 0]] if len(img.shape) > 2 else img @@ -556,3 +532,4 @@ def CompressImage(in_img: np.ndarray, path: str, max_size: int = 16 * LIT.KBYTE, mission_Table = {0: TakePhotoStarAnalysis, 1: takePictureWithParameters, 2: crop_and_compress, 3: makeIcon, 4: StarAnalysis, 5: testing} + diff --git a/software/SAT0_OBC/SatImageTaking/utils.py b/software/SAT0_OBC/SatImageTaking/utils.py index c9fbf7f..cec8a03 100644 --- a/software/SAT0_OBC/SatImageTaking/utils.py +++ b/software/SAT0_OBC/SatImageTaking/utils.py @@ -17,11 +17,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ''' -from cv2 import imwrite as cv2_imwrite -from cv2 import imread as cv2_imread -from cv2 import resize as cv2_resize -from cv2 import INTER_AREA as cv2_INTER_AREA -from cv2 import IMWRITE_JPEG_QUALITY + +# 17.1.2024 asaf h: changed cv2 import cv2_ >> cv2. +import cv2 + from datetime import datetime import os import numpy as np @@ -45,7 +44,7 @@ def get_image(path_or_image: str or np.ndarray, x=-1, dim=None) -> np.ndarray: image = path_or_image elif isinstance(path_or_image, int): path = f"./sent/{path_or_image}/Img.jpeg" - image = cv2_imread(path, -1) + image = cv2.imread(path, -1) else: raise TypeError("pass path or image") return image @@ -62,9 +61,9 @@ def _read_image(path: str, x=-1, dim=None) -> np.ndarray: Returns: np.ndarray: Image. """ - image = cv2_imread(path, x) + image = cv2.imread(path, x) if dim and len(dim): - image = cv2_resize(image, dim) + image = cv2.resize(image, dim) return image @@ -78,7 +77,8 @@ def get_pic_path_list(path: str) -> list: list: List of path of images in the directory. """ file_list = os.listdir(path) - pic_path_list = [path + i for i in file_list if i.endswith(('.png', '.jpg', '.jpeg', '.jfif')) and + # 19.9.2023 asaf h: fix bug + pic_path_list = [path + '/' + i for i in file_list if i.endswith(('.png', '.jpg', '.jpeg', '.jfif')) and not i.startswith('.')] return pic_path_list @@ -172,11 +172,11 @@ def save_pic(pic_path: str, image: np.ndarray, compress_factor=95, dim=None) -> None """ if dim and len(dim) == 2: - image = cv2_resize(image, dim) + image = cv2.resize(image, dim) image_name = datetime.now().strftime("%Y-%m-%d %H-%M-%S-%f") + '.jpeg' image_path = os.path.join(pic_path, image_name) - compress = [IMWRITE_JPEG_QUALITY, compress_factor] - cv2_imwrite(image_path, image, compress) + compress = [cv2.IMWRITE_JPEG_QUALITY, compress_factor] + cv2.imwrite(image_path, image, compress) def resize(img: np.ndarray, dim=(60, 60)) -> np.ndarray: @@ -189,5 +189,5 @@ def resize(img: np.ndarray, dim=(60, 60)) -> np.ndarray: Returns: np.ndarray: The resized image. """ - new_img = cv2_resize(img, dim, Interpolation=cv2_INTER_AREA) + new_img = cv2.resize(img, dim, Interpolation=cv2.INTER_AREA) return new_img diff --git a/software/SAT0_OBC/controller.py b/software/SAT0_OBC/controller.py index c031c42..88841d3 100644 --- a/software/SAT0_OBC/controller.py +++ b/software/SAT0_OBC/controller.py @@ -254,7 +254,7 @@ def msgArrived(self, msgByte): elif command == CmdTypes.CMD_UPLOAD_FILE.value: # 16 print("*** CmdTypes.CMD_UPLOAD_FILE ***") - + # set state to in mission and sendmsg self.state = StateTypes.STATE_BUSY.value self.serial.sendMsg(bytearray([ApiTypes.API_ACK.value])) @@ -273,18 +273,18 @@ def msgArrived(self, msgByte): scriptNum = paramsList[1] line_num = paramsList[2] num_chars = paramsList[3] - + add = lambda i: chr(int(hex(i), 16)) txt = ''.join([add(i) for i in paramsList[4: 4 + num_chars]]) - - reset = 1 if len(paramsList[: 4 + num_chars]) < len(paramsList) and paramsList[-1] != 0 else 0 #TODO how to reset + # 19.9.2023 asaf h: To reset the file it is better to send 1 instead of not 0. + reset = 1 if len(paramsList[: 4 + num_chars]) < len(paramsList) and paramsList[-1] == 1 else 0 # execute mission with parameters: - print('parms: * > * > ' , f'missionCount: {missionCount}, scriptNum: {scriptNum}, ', end='') + print('parms: * > * > ' , f'missionCount: {missionCount}, scriptNum: {scriptNum}, ', end='') print(f'line_num: {line_num}, num_chars: {num_chars}, txt: {repr(txt)}, reset: {reset}') - + import uploading uploading.main(missionCount, scriptNum, line_num, txt, reset) - + # finished executing mission: self.state = StateTypes.STATE_READY.value print("*** CmdTypes.CMD_UPLOAD_FILE Done ***") diff --git a/software/SAT0_OBC/uploading.py b/software/SAT0_OBC/uploading.py index 4a4a576..71e67e1 100644 --- a/software/SAT0_OBC/uploading.py +++ b/software/SAT0_OBC/uploading.py @@ -1,6 +1,7 @@ import numpy as np import os + def insert_line(script: list, line_num: int, line: int) -> None: x = -1 if script[-1] == '#Execute script' else 0 line_num -= 1 # cuz main(): @@ -15,8 +16,8 @@ def insert_line(script: list, line_num: int, line: int) -> None: for i in range(len(script) + x, line_num + 2)] script.insert(line_num, line) - -def writing_file(file_num: int, num_line: int, script_txt: str, file_path) -> None: + # 19.9.2023 asaf h: file_num not in use, it is on file_path, changed also line 148. +def writing_file(num_line: int, script_txt: str, file_path) -> None: # defultive script """ def main(): @@ -24,7 +25,9 @@ def main(): if __name__ == "__main__" main() """ - Tab = ' '*4 + # 19.9.2023 asaf h: enable to insert multiple lines at once. + script_txt = script_txt.replace('\n', '\n\t').strip('\t') + Tab = '\t' line = Tab + script_txt num_line += 1 # cuz main(): @@ -56,7 +59,7 @@ def reset_file(script_path: str) -> None: f.writelines(file) -def execute_file(file_name: str, script_path:str) -> list: +def execute_file(file_name: str, script_path: str) -> list: def module_from_file(module_name, file_path): import importlib.util @@ -77,20 +80,22 @@ def module_from_file(module_name, file_path): eror = format_tb(exception_traceback)[-1] print(eror) line_num = int(eror[eror.find(',') + 1: eror.rfind(',') - ].replace('line', '').strip()) + ].replace('line', '').strip()) if isinstance(exception_object, SyntaxError): # bypass problem in that eror line_num = exception_object.lineno run_success = False - line_num -= 1 # cuz main() + line_num -= 1 # cuz main() info = [run_success, file_name, line_num] print(info) return info -def check_execute(file_name, script_line, script_path): - if script_line.find('\n') == -1: +# 19.9.2023 asaf h: file_num not in use, it is on file_path, changed also line 151. +def check_execute(script_line, script_path): + # 19.9.2023 asaf h: enable to insert multiple lines at once. + if not script_line.endswith("\n"): return True with open(script_path, 'r') as f: last_line = f.readlines()[-1] @@ -100,7 +105,7 @@ def check_execute(file_name, script_line, script_path): def writeMetaFile(mission_count: int, data: list) -> list: run_success, file_name = data[:2] if run_success: - chars_length, result = data[2:] + chars_length, result = data[2:] else: eror_line_num = data[2:] @@ -109,7 +114,7 @@ def writeMetaFile(mission_count: int, data: list) -> list: file_num = int(''.join(i for i in file_name if i.isdigit())) with open(FINAL_PATH, 'wb+') as f: - hex_byte_trans = lambda x: np.array(x).astype(np.uint8).tobytes() + def hex_byte_trans(x): return np.array(x).astype(np.uint8).tobytes() b_run_success = hex_byte_trans(run_success) b_file_num = hex_byte_trans(file_num) @@ -122,30 +127,29 @@ def writeMetaFile(mission_count: int, data: list) -> list: f.write(b_chars_length) f.write(bytes(result, 'utf-8')) - print(b_run_success + b_file_num + b_chars_length + bytes(result, 'utf-8')) + print(b_run_success + b_file_num + + b_chars_length + bytes(result, 'utf-8')) else: b_eror_line_num = hex_byte_trans(eror_line_num) f.write(b_eror_line_num) print(b_run_success + b_file_num + b_eror_line_num) - - def main(mission_count: int, script_num: int, line_num: int, txt: str, reset): path = "/home/pi/scripts_folder/" if isinstance(path, str) and not os.path.exists(path): - os.makedirs(path) - + os.makedirs(path) + file_name = 'script' + str(script_num) + '.py' script_path = path + file_name if reset or not os.path.exists(script_path): reset_file(script_path) - - writing_file(script_num, line_num, txt, script_path) - - if check_execute(file_name, txt, script_path): + # 19.9.2023 asaf h: file_num not in use, it is on file_path, changed also line 20. + writing_file(line_num, txt, script_path) + # 19.9.2023 asaf h: file_num not in use, it is on file_path, changed also line 95. + if check_execute(txt, script_path): import time time.sleep(0.5) info = execute_file(file_name, script_path) @@ -153,10 +157,8 @@ def main(mission_count: int, script_num: int, line_num: int, txt: str, reset): else: FINAL_PATH = f"./outbox/{mission_count}/_metaUploadingFile.bin" with open(FINAL_PATH, 'wb+') as f: - f.write(np.array(line_num).astype(np.uint8).tobytes()) - print(f'changed line: {line_num}, {np.array(line_num).astype(np.uint8).tobytes()}') - - - -if __name__ == "__main__": - main() + f.write(np.array(script_num).astype(np.uint8).tobytes()) + f.write(np.array(line_num).astype(np.uint8).tobytes()) + print( + f'changed line: {line_num}, {np.array(line_num).astype(np.uint8).tobytes()}') +# 19.9.2023 asaf h: no need of if __name__ == "__main__":.