diff --git a/src/vstarstack/library/photometry/star_background.py b/src/vstarstack/library/photometry/star_background.py
new file mode 100644
index 0000000..31a8149
--- /dev/null
+++ b/src/vstarstack/library/photometry/star_background.py
@@ -0,0 +1,75 @@
+#
+# Copyright (c) 2025 Vladislav Tsendrovskii
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+from typing import Tuple
+import numpy as np
+import cv2
+from vstarstack.library.data import DataFrame
+
+def calculate_bg(image : np.ndarray, x : int, y : int, star_r : int, bg_r_big : int, bg_r_small : int) -> dict:
+ star_mask = np.zeros(image.shape)
+ cv2.circle(star_mask, (x,y), star_r, 1, -1)
+ bg_mask = np.zeros(image.shape)
+ cv2.circle(bg_mask, (x,y), bg_r_big, 1, -1)
+ cv2.circle(bg_mask, (x,y), bg_r_small, 0, -1)
+
+ bg_pixels = image * bg_mask
+ bg_sum = np.sum(bg_pixels)
+ bg_npix = np.sum(bg_mask)
+ bg_mean = bg_sum / bg_npix
+
+ star_pixels = (image - bg_mean) * star_mask
+ star_sum = np.sum(star_pixels)
+ star_npix = np.sum(star_mask)
+ return {
+ "star" : star_sum,
+ "star_npix" : star_npix,
+ "bg" : bg_sum,
+ "bg_npix" : bg_npix
+ }
+
+def calculate_bgs(image : np.ndarray, stars : list) -> list:
+ bgs = []
+ for star in stars:
+ x = star["x"]
+ y = star["y"]
+ r = int(star["radius"])
+ bg = calculate_bg(image, x, y, r, 2*r, 4*r)
+ bgs.append(bg)
+ return bgs
+
+def calculate_bgs_df(df : DataFrame, stars : list) -> Tuple[list, set]:
+ values = []
+ all_channels = set()
+ for star in stars:
+ channels = {
+ "x" : star["x"],
+ "y" : star["y"],
+ "radius" : star["radius"],
+ "channels" : {},
+ }
+ for channel in df.get_channels():
+ if not df.get_channel_option(channel, "brightness"):
+ continue
+ all_channels.add(channel)
+ image, _ = df.get_channel(channel)
+ x = star["x"]
+ y = star["y"]
+ sr = int(star["radius"]+0.5)
+ bgr_small = sr * 2
+ bgr_big = sr * 3
+ bg = calculate_bg(image, x, y, sr, bgr_big, bgr_small)
+ channels["channels"][channel] = bg
+ values.append(channels)
+ return values, all_channels
diff --git a/src/vstarstack/tool/photometry/measure_pollution.py b/src/vstarstack/tool/photometry/measure_pollution.py
new file mode 100644
index 0000000..137616a
--- /dev/null
+++ b/src/vstarstack/tool/photometry/measure_pollution.py
@@ -0,0 +1,75 @@
+#
+# Copyright (c) 2025 Vladislav Tsendrovskii
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+import json
+import csv
+
+from vstarstack.library.photometry.star_background import calculate_bgs_df
+from vstarstack.library.data import DataFrame
+
+
+def process_image(project, argv : list):
+ image_path = argv[0]
+ stars_path = argv[1]
+ report_fname = argv[2]
+ with open(stars_path) as f:
+ stars = json.load(f)
+ stars = [{"x" : star["keypoint"]["x"], "y" : star["keypoint"]["y"], "radius" : star["keypoint"]["radius"]} for star in stars["points"]]
+
+ df = DataFrame.load(image_path)
+ backgrounds, channels = calculate_bgs_df(df, stars)
+ with open(report_fname, "w", encoding="utf-8") as f:
+ writer = csv.writer(f)
+ header1 = ["x", "y", "radius","","name","mag","lum","", "star pixels", "background pixels",""]
+ header2 = ["","","","","","","","","","",""]
+ for channel in channels:
+ header2.append(channel)
+ header2.append("")
+ header2.append("")
+ header1 += ["star value", "background value", ""]
+ header2 += ["Total", "", "", ""]
+ header1 += ["star value", "background value", "mean star pixel", "mean background pixel"]
+ writer.writerow(header2)
+ writer.writerow(header1)
+
+ for item in backgrounds:
+ bg_npix = 0
+ star_npix = 0
+ row = None
+ sum_star = 0
+ sum_bg = 0
+ for channel in channels:
+ if row is None:
+ mag = ""
+ lum = ""
+ name = ""
+ row = [item["x"], item["y"], int(item["radius"]+0.5), "", name, mag, lum, ""]
+ star_npix = int(item["channels"][channel]["star_npix"])
+ bg_npix = int(item["channels"][channel]["bg_npix"])
+ row += [star_npix, bg_npix, ""]
+ row += [int(item["channels"][channel]["star"])]
+ row += [int(item["channels"][channel]["bg"])]
+ row += [""]
+
+ sum_star += int(item["channels"][channel]["star"])
+ sum_bg += int(item["channels"][channel]["bg"])
+
+ row += [sum_star, sum_bg, int(sum_star/star_npix), int(sum_bg/bg_npix)]
+ writer.writerow(row)
+
+commands = {
+ "image": (process_image,
+ "build report for clusters",
+ "image.zip stars.json report.csv"),
+}
diff --git a/src/vstarstack/tool/photometry/photometry.py b/src/vstarstack/tool/photometry/photometry.py
index ce1ac40..4ebbe9a 100644
--- a/src/vstarstack/tool/photometry/photometry.py
+++ b/src/vstarstack/tool/photometry/photometry.py
@@ -15,4 +15,5 @@
commands = {
"measure-mag" : ("vstarstack.tool.photometry.measure_mag", "measure star magnitude"),
"measure-fwhm" : ("vstarstack.tool.photometry.measure_fwhm", "measure full width at half maximum"),
+ "measure-background" : ("vstarstack.tool.photometry.measure_pollution", "measure background light level"),
}