Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 38 additions & 50 deletions software/SAT0_OBC/SatImageTaking/earth.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,9 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
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
Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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):
"""
Expand All @@ -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
Expand All @@ -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):
"""
Expand All @@ -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
79 changes: 29 additions & 50 deletions software/SAT0_OBC/SatImageTaking/star_finder.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
'''
"""
@file star_finder.py
@brief A file for the finding stars function.

Expand All @@ -16,22 +16,10 @@

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
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
Expand All @@ -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.
"""
Expand All @@ -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()

Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
"""
Expand Down
Loading