diff --git a/.idea/CABC4L.iml b/.idea/CABC4L.iml
index d870a4a..eab80d1 100644
--- a/.idea/CABC4L.iml
+++ b/.idea/CABC4L.iml
@@ -1,8 +1,10 @@
-
-
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index ab530bf..850ef55 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/capture.py b/capture.py
index 324b8c2..53d5edf 100644
--- a/capture.py
+++ b/capture.py
@@ -12,6 +12,7 @@
gi.require_version('Gst', '1.0')
from gi.repository import GObject, Gst
from multiprocessing.connection import Listener
+from time import sleep
DBusGMainLoop(set_as_default=True)
Gst.init(None)
@@ -73,10 +74,11 @@ def play_pipewire_stream(node_id):
maxcheckpoint = 100
immersive_multiplier = 1.8
offset = 0
- mode = 'normal'
+ mode = 'disabled'
running = True
listener = Listener(address=("localhost", 21827))
+ blistener = Listener(address=("localhost", 21828))
empty_dict = dbus.Dictionary(signature="sv")
fd_object = portal.OpenPipeWireRemote(session, empty_dict,
@@ -94,11 +96,11 @@ def play_pipewire_stream(node_id):
# Read and display frames from the pipeline
conn = listener.accept()
+ connb = blistener.accept()
backlight = int(subprocess.check_output("light -r", shell=True))
while running:
while conn.poll():
msg = conn.recv()
- print(f"got message: {msg}")
if msg == "EXIT":
running = False
@@ -106,53 +108,66 @@ def play_pipewire_stream(node_id):
margin = int(msg[1])
elif msg[0] == "target":
target = int(msg[1])
- ret, frame = cap.read()
-
- if not ret:
- print('=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')
- break
-
-
- # Calculate the overall image of the grayscale frame cv2.resize(frame, (100, 100))
- image = float(cv2.cvtColor(cv2.resize(frame, (200, 200)), cv2.COLOR_BGR2GRAY).mean()) # by resizing the image , the program only cost 0.3-0.5w on my r7
- if checkcnt >= checkpoint:
- checkcnt = 0
- buff = int(subprocess.check_output("light -r", shell=True))
- if buff == backlight:
- if checkpoint != maxcheckpoint:
- checkpoint = maxcheckpoint
- else:
- pass
- else :
- checkpoint = 1
- print('something is wrong, backlight is : '+str(buff) + ' instead of '+str(backlight)+', trying to set again')
- backlight = buff
- #print("target : "+str(target)+" Content: "+str(image)+ " backlight: "+str(backlight))
- if mode == 'normal':
- if int(image) <= 1:
- if backlight == 0:
- pass
- else:
- subprocess.call(('light', '-S', '-r', str(0)))
- backlight = 0
-
- elif abs((backlight*image)//255 - target) > margin:
- val = int((target/image)*255)
- if val > 255:
- subprocess.call(('light', '-S', '-r', '255'))
+ elif msg[0] == "mode":
+ mode = str(msg[1])
+ elif msg[0] == "offset":
+ offset = int(msg[1])
+ elif msg[0] == "multiplier":
+ immersive_multiplier = float(msg[1])
+ if mode != "disabled":
+ while connb.poll():
+ msg = connb.recv()
+ if msg == "value":
+ connb.send(backlight)
+ ret, frame = cap.read()
+
+ if not ret:
+ print('=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')
+ break
+
+
+ # Calculate the overall image of the grayscale frame cv2.resize(frame, (100, 100))
+ image = float(cv2.cvtColor(cv2.resize(frame, (200, 200)), cv2.COLOR_BGR2GRAY).mean()) # by resizing the image , the program only cost 0.3-0.5w on my r7
+ if checkcnt >= checkpoint:
+ checkcnt = 0
+ buff = int(subprocess.check_output("light -r", shell=True))
+ if buff == backlight:
+ if checkpoint != maxcheckpoint:
+ checkpoint = maxcheckpoint
+ else:
+ pass
+ else :
+ checkpoint = 1
+ print('something is wrong, backlight is : '+str(buff) + ' instead of '+str(backlight)+', trying to set again')
+ backlight = buff
+ #print("target : "+str(target)+" Content: "+str(image)+ " backlight: "+str(backlight))
+ if mode == 'normal':
+ if int(image) <= 1:
+ if backlight == 0:
+ pass
+ else:
+ subprocess.call(('light', '-S', '-r', str(0)))
+ backlight = 0
+
+ elif abs((backlight*image)//255 - target) > margin:
+ val = int((target/image)*255)
+ if val > 255:
+ subprocess.call(('light', '-S', '-r', '255'))
+ backlight = 255
+ else:
+ subprocess.call(('light','-S', '-r', str(val)))
+ backlight = val
+ elif mode == 'immersive':
+ if int(image*immersive_multiplier)+offset > 255:
+ subprocess.call(('light', '-S', '-r', str(255)))
backlight = 255
else:
- subprocess.call(('light','-S', '-r', str(val)))
- backlight = val
- elif mode == 'immersive':
- if int(image*immersive_multiplier)+offset > 255:
- subprocess.call(('light', '-S', '-r', str(255)))
- backlight = 255
- else:
- backlight = int(image*immersive_multiplier+offset)
- subprocess.call(('light','-S', '-r', str(backlight)))
- print(backlight)
- checkcnt += 1
+ backlight = int(image*immersive_multiplier+offset)
+ subprocess.call(('light','-S', '-r', str(backlight)))
+ #print(backlight)
+ checkcnt += 1
+ else:
+ sleep(0.5)
# Release the VideoCapture object and close windows
cap.release()
cv2.destroyAllWindows()
diff --git a/main.py b/main.py
index be4f944..a090a56 100644
--- a/main.py
+++ b/main.py
@@ -4,23 +4,36 @@
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw
from multiprocessing.connection import Client
+from threading import Thread
+from time import sleep
class MainWindow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
+ self.brunning = True
+ self.running = True
super().__init__(*args, **kwargs)
self.Mainbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
- self.SliderBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ self.NormalBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ self.ImmersiveBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+
+ self.lightbar = Gtk.LevelBar(orientation=Gtk.Orientation.HORIZONTAL)
+ self.lightbar.set_min_value(0)
+ self.lightbar.set_max_value(255)
+
self.set_child(self.Mainbox)
+ self.initModeSelect()
+ self.initMultiplier()
+ self.initOffset()
self.initTarget()
self.initMargin()
- self.Mainbox.append(self.SliderBox)
+ #self.Mainbox.append(self.NormalBox)
self.header = Gtk.HeaderBar()
self.set_titlebar(self.header)
self.button = Gtk.Button(label="Hello")
- self.header.pack_start(self.button)
+ #self.header.pack_start(self.button)
self.button.connect('clicked', self.hello)
while True:
try:
@@ -28,7 +41,153 @@ def __init__(self, *args, **kwargs):
break
except:
pass
+ while True:
+ try:
+ self.bconn = Client(address=("localhost", 21828))
+ break
+ except:
+ pass
+ self.connect("close-request", self.closea)
+
+ Bart = Thread(target=self.bars)
+ Bart.start()
+
+ def closea(self,arg):
+ self.brunning = False
+ self.running = False
+ self.close()
+
+ def initModeSelect(self):
+ self.buttonBox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
+ self.radio1 = Gtk.CheckButton(label="disable")
+ self.radio2 = Gtk.CheckButton(label="normal")
+ self.radio3 = Gtk.CheckButton(label="cinema(lcd)")
+ self.radio2.set_group(self.radio1)
+ self.radio3.set_group(self.radio1)
+ self.radio1.connect("toggled", self.toggled)
+ self.radio2.connect("toggled", self.toggled)
+ self.radio3.connect("toggled", self.toggled)
+ self.buttonBox.append(self.radio1)
+ self.buttonBox.append(self.radio2)
+ self.buttonBox.append(self.radio3)
+ self.radio1.set_active(True)
+ self.Mainbox.append(self.buttonBox)
+
+ def toggled(self,arg):
+ if self.radio1.get_active():
+ self.brunning = False
+ try:
+ self.conn.send(["mode","disabled"])
+ except:
+ pass
+
+ try:
+ self.Mainbox.remove(self.NormalBox)
+ except:
+ pass
+
+ try:
+ self.Mainbox.remove(self.ImmersiveBox)
+ except:
+ pass
+
+ try:
+ self.Mainbox.remove(self.lightbar)
+ except:
+ pass
+
+ elif self.radio2.get_active():
+ self.conn.send(["mode","normal"])
+ self.brunning = True
+ try:
+ self.Mainbox.remove(self.ImmersiveBox)
+ except:
+ pass
+
+ try:
+ self.Mainbox.remove(self.lightbar)
+ self.Mainbox.append(self.lightbar)
+ except:
+ self.Mainbox.append(self.lightbar)
+
+ self.Mainbox.append(self.NormalBox)
+
+ elif self.radio3.get_active():
+ self.conn.send(["mode","immersive"])
+ self.brunning = True
+ try:
+ self.Mainbox.remove(self.NormalBox)
+ except:
+ pass
+
+ try:
+ self.Mainbox.remove(self.lightbar)
+ self.Mainbox.append(self.lightbar)
+ except:
+ self.Mainbox.append(self.lightbar)
+
+ self.Mainbox.append(self.ImmersiveBox)
+ def bars(self):
+ while self.running:
+ while self.brunning :
+ self.bconn.send("value")
+ try :
+ self.lightbar.set_value(self.bconn.recv())
+ except:
+ try:
+ self.Mainbox.remove(self.lightbar)
+ self.Mainbox.append(self.lightbar)
+ except:
+ self.Mainbox.append(self.lightbar)
+ sleep(0.03)
+ sleep(0.5)
+ def initOffset(self):
+ self.offsetbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ self.offsetadj = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
+ self.offsetframe = Gtk.Frame()
+ self.offsetframe.set_margin_top(15)
+ self.offsetframe.set_margin_end(10)
+ self.offsetframe.set_child(self.offsetbox)
+ self.offsetlabel = Gtk.Label()
+ self.offsetlabel.set_margin_top(10)
+ self.offsetlabel.set_label("offset")
+
+ self.offsetslider = Gtk.Scale()
+ self.offsetslider.set_digits(2) # Number of decimal places to use
+ self.offsetslider.set_range(0, 254)
+ self.offsetslider.set_draw_value(True) # Show a label with current value
+ self.offsetslider.set_value(1) # Sets the current value/position
+ self.offsetslider.connect('value-changed', self.offsetslider_changed)
+ self.offsetslider.set_hexpand(True) #
+
+ self.offsetadj.append(self.offsetslider)
+ self.offsetbox.append(self.offsetlabel)
+ self.offsetbox.append(self.offsetadj)
+ self.ImmersiveBox.append(self.offsetframe)
+ def initMultiplier(self):
+ self.multiplierbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ self.multiplieradj = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
+ self.multiplierframe = Gtk.Frame()
+ self.multiplierframe.set_margin_top(15)
+ self.multiplierframe.set_margin_end(10)
+ self.multiplierframe.set_child(self.multiplierbox)
+ self.multiplierlabel = Gtk.Label()
+ self.multiplierlabel.set_margin_top(10)
+ self.multiplierlabel.set_label("multiplier")
+
+ self.multiplierslider = Gtk.Scale()
+ self.multiplierslider.set_digits(2) # Number of decimal places to use
+ self.multiplierslider.set_range(0.1, 4)
+ self.multiplierslider.set_draw_value(True) # Show a label with current value
+ self.multiplierslider.set_value(1) # Sets the current value/position
+ self.multiplierslider.connect('value-changed', self.multiplierslider_changed)
+ self.multiplierslider.set_hexpand(True) #
+
+ self.multiplieradj.append(self.multiplierslider)
+ self.multiplierbox.append(self.multiplierlabel)
+ self.multiplierbox.append(self.multiplieradj)
+ self.ImmersiveBox.append(self.multiplierframe)
def initTarget(self):
self.targetbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
@@ -52,7 +211,7 @@ def initTarget(self):
self.targetadj.append(self.targetslider)
self.targetbox.append(self.targetlabel)
self.targetbox.append(self.targetadj)
- self.SliderBox.append(self.targetframe)
+ self.NormalBox.append(self.targetframe)
def initMargin(self):
self.marginbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
@@ -76,11 +235,17 @@ def initMargin(self):
self.marginadj.append(self.marginslider)
self.marginbox.append(self.marginlabel)
self.marginbox.append(self.marginadj)
- self.SliderBox.append(self.marginframe)
+ self.NormalBox.append(self.marginframe)
def targetslider_changed(self, slider):
self.conn.send(["target",int(self.targetslider.get_value())])
+ def multiplierslider_changed(self, slider):
+ self.conn.send(["multiplier",float(self.multiplierslider.get_value())])
+
+ def offsetslider_changed(self, slider):
+ self.conn.send(["offset",int(self.offsetslider.get_value())])
+
def marginslider_changed(self, slider):
self.conn.send(["margin",int(self.marginslider.get_value())])
diff --git a/readme.md b/readme.md
index 6d63632..31889fc 100644
--- a/readme.md
+++ b/readme.md
@@ -1,7 +1,20 @@
-Content Adaptive Brightness Control for linux
+# Content-Based Adaptive Brightness Control for Linux
+## needed
-need `light` package to work and python opencv + pyGobject libs
+need `light` package to work and python `opencv` + `pyGobject` libs
-state : WORKING ON A CONTRAST BOOST MODE FOR LCD SCREENS, IT ALLOWS TO MAKE IPS'S PANNELS ALMOST AS GOOD AS AMOLED FOR WATCHING VIDEOS/FILMS
+## how to use
+run `start.py` in a terminal, hit `ctrl+c` to close
+### modes
+#### normal
+As you saw on Windows 11, to maintain a 'constant light level' to not blow your eyes up
+#### cinema
+Only usefull for lcd-based screens
+adapts backlight in realtime with content brightness to make dark scenes dark and bright scenes bright
+recommend using it in a dark environnement
+best to watch films/videos
-run `start.py`
\ No newline at end of file
+## incoming improvements
+- a smart mode for cinema to adjust multiplier and offset automatically
+- an installer
+- some stability and performance improvements
\ No newline at end of file
diff --git a/start.py b/start.py
index c6aabaf..93cb27a 100644
--- a/start.py
+++ b/start.py
@@ -14,5 +14,6 @@ def gui():
Pdaemon.start()
sleep(0.5)
Pgui.start()
-Pdaemon.join()
Pgui.join()
+Pdaemon.close()
+