Pages

Saturday, December 14, 2024

Fedora 41 : OpenCV example with PyQt6.

Today I tested another source code with opencv, numpy, PyQt6 python packages.
I install opencv python package with dnf5 tool:
root@localhost:/home/mythcat# dnf5 install  python3-opencv.x86_64
The source code let you to open, change a image and save using sliders and a reset option.
This is the source code:
import sys
import cv2
import numpy as np
from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QSlider, QFileDialog, QPushButton, QHBoxLayout
from PyQt6.QtGui import QImage, QPixmap
from PyQt6.QtCore import Qt, pyqtSlot

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Real-Time Color Selection")
        self.setGeometry(100, 100, 1200, 800)

        # Create central widget and main layout
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)

        # Create image label
        self.image_label = QLabel()
        main_layout.addWidget(self.image_label)

        # Initialize sliders
        self.lower_h = QSlider(Qt.Orientation.Horizontal)
        self.lower_s = QSlider(Qt.Orientation.Horizontal)
        self.lower_v = QSlider(Qt.Orientation.Horizontal)
        self.upper_h = QSlider(Qt.Orientation.Horizontal)
        self.upper_s = QSlider(Qt.Orientation.Horizontal)
        self.upper_v = QSlider(Qt.Orientation.Horizontal)

        # Set slider ranges
        for slider in [self.lower_h, self.upper_h]:
            slider.setRange(0, 179)
        for slider in [self.lower_s, self.lower_v, self.upper_s, self.upper_v]:
            slider.setRange(0, 255)

        # Set initial slider values
        self.lower_h.setValue(50)
        self.lower_s.setValue(100)
        self.lower_v.setValue(50)
        self.upper_h.setValue(130)
        self.upper_s.setValue(255)
        self.upper_v.setValue(255)

        # Connect sliders to update function
        self.lower_h.valueChanged.connect(self.update_hsv_range)
        self.lower_s.valueChanged.connect(self.update_hsv_range)
        self.lower_v.valueChanged.connect(self.update_hsv_range)
        self.upper_h.valueChanged.connect(self.update_hsv_range)
        self.upper_s.valueChanged.connect(self.update_hsv_range)
        self.upper_v.valueChanged.connect(self.update_hsv_range)

        # Create slider layouts with labels
        sliders_layout = QVBoxLayout()
        
        # Add slider pairs with labels
        slider_pairs = [
            ("Lower Hue", self.lower_h),
            ("Lower Saturation", self.lower_s),
            ("Lower Value", self.lower_v),
            ("Upper Hue", self.upper_h),
            ("Upper Saturation", self.upper_s),
            ("Upper Value", self.upper_v)
        ]

        for label_text, slider in slider_pairs:
            row_layout = QHBoxLayout()
            label = QLabel(label_text)
            label.setMinimumWidth(120)
            row_layout.addWidget(label)
            row_layout.addWidget(slider)
            sliders_layout.addLayout(row_layout)

        main_layout.addLayout(sliders_layout)

        # Add buttons
        button_layout = QHBoxLayout()
        
        self.reset_button = QPushButton("Reset Values")
        self.reset_button.clicked.connect(self.reset_values)
        button_layout.addWidget(self.reset_button)

        self.open_image_button = QPushButton("Open Image")
        self.open_image_button.clicked.connect(self.open_image)
        button_layout.addWidget(self.open_image_button)

        self.save_button = QPushButton("Save Image")
        self.save_button.clicked.connect(self.save_image)
        button_layout.addWidget(self.save_button)
        main_layout.addLayout(button_layout)

        # Process initial image
        self.process_image()

    def process_image(self):
        image_bgr = cv2.imread("image.png")
        if image_bgr is None:
            image_bgr = cv2.imread("default_image.png")
        
        self.image_bgr = image_bgr
        self.image_hsv = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2HSV)

        # Create initial mask using current slider values
        lower_values = np.array([self.lower_h.value(), self.lower_s.value(), self.lower_v.value()])
        upper_values = np.array([self.upper_h.value(), self.upper_s.value(), self.upper_v.value()])
        
        mask_test = cv2.inRange(self.image_hsv, lower_values, upper_values)
        image_bgr_masked = cv2.bitwise_and(image_bgr, image_bgr, mask=mask_test)
        self.image_rgb = cv2.cvtColor(image_bgr_masked, cv2.COLOR_BGR2RGB)
        self.update_image()

    def update_image(self):
        height, width, channel = self.image_rgb.shape
        bytes_per_line = width * channel
        q_image = QImage(self.image_rgb.data, width, height, bytes_per_line, QImage.Format.Format_RGB888)
        pixmap = QPixmap.fromImage(q_image)
        self.image_label.setPixmap(pixmap.scaled(700, 500, Qt.AspectRatioMode.KeepAspectRatio))

    def update_hsv_range(self):
        lower_values = np.array([self.lower_h.value(), self.lower_s.value(), self.lower_v.value()])
        upper_values = np.array([self.upper_h.value(), self.upper_s.value(), self.upper_v.value()])
        mask_test = cv2.inRange(self.image_hsv, lower_values, upper_values)
        image_bgr_masked = cv2.bitwise_and(self.image_bgr, self.image_bgr, mask=mask_test)
        self.image_rgb = cv2.cvtColor(image_bgr_masked, cv2.COLOR_BGR2RGB)
        self.update_image()

    def reset_values(self):
        self.lower_h.setValue(50)
        self.lower_s.setValue(100)
        self.lower_v.setValue(50)
        self.upper_h.setValue(130)
        self.upper_s.setValue(255)
        self.upper_v.setValue(255)

    def open_image(self):
        filename, _ = QFileDialog.getOpenFileName(self, "Select Image File", "", "Image Files (*.png *.jpg *.jpeg)")
        if filename:
            self.image_bgr = cv2.imread(filename)
            if self.image_bgr is not None:
                self.image_hsv = cv2.cvtColor(self.image_bgr, cv2.COLOR_BGR2HSV)
                self.update_hsv_range()  # This will apply current filter and update display

    def save_image(self):
        filename, _ = QFileDialog.getSaveFileName(self, "Save Image", "", "PNG Files (*.png);;JPEG Files (*.jpg)")
        if filename:
            # Make sure filename has an extension
            if not filename.endswith(('.png', '.jpg', '.jpeg')):
                filename += '.png'
            # Convert and save
            output_image = cv2.cvtColor(self.image_rgb, cv2.COLOR_RGB2BGR)
            cv2.imwrite(filename, output_image)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

Thursday, December 12, 2024

Fedora 41 : The cirq python package cannot be install - build errors

Today, I tried to use cirq python package on Fedora 41. This need the python version 3.12.
root@localhost:/home/mythcat# dnf install python3.12.x86_6c
I got some errors because ask me to install the rust:
root@localhost:/home/mythcat# curl https://sh.rustup.rs -sSf | sh
info: downloading installer

Welcome to Rust!

This will download and install the official compiler for the Rust
programming language, and its package manager, Cargo.

Rustup metadata and toolchains will be installed into the Rustup
home directory, located at:
...
Rust is installed now. Great!

To get started you may need to restart your current shell.
This would reload your PATH environment variable to include
Cargo's bin directory ($HOME/.cargo/bin).

To configure your current shell, you need to source
the corresponding env file under $HOME/.cargo.

This is usually done by running one of the following (note the leading DOT):
. "$HOME/.cargo/env"            # For sh/bash/zsh/ash/dash/pdksh
source "$HOME/.cargo/env.fish"  # For fish
These are the next steps I used:
root@localhost:/home/mythcat# pip install --upgrade pip
...
root@localhost:/home/mythcat# pip install maturin
...
root@localhost:/home/mythcat# pip install cirq
...
Collecting cirq
  Using cached cirq-1.4.1-py3-none-any.whl.metadata (7.4 kB)
...
        cargo:rerun-if-env-changed=PYO3_USE_ABI3_FORWARD_COMPATIBILITY
      
        --- stderr
        error: the configured Python interpreter version (3.13) is newer than PyO3's maximum supported version (3.12)
        = help: please check if an updated version of PyO3 is available. Current version: 0.20.3
        = help: set PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 to suppress this check and build anyway using the stable ABI
      💥 maturin failed
        Caused by: Failed to build a native library through cargo
        Caused by: Cargo build finished with "exit status: 101": `env -u CARGO PYO3_ENVIRONMENT_SIGNATURE="cpython-3.13-64bit" PYO3_PYTHON="/usr/bin/python3" PYTHON_SYS_EXECUTABLE="/usr/bin/python3" "cargo" "rustc" "--features" "pyo3/extension-module" "--message-format" "json-render-diagnostics" "--manifest-path" "/tmp/pip-install-v0zuc831/quil_ebc006157dbd433dbc0890811ee80748/quil-py/Cargo.toml" "--release" "--lib" "--crate-type" "cdylib"`
      Error: command ['maturin', 'pep517', 'build-wheel', '-i', '/usr/bin/python3', '--compatibility', 'off'] returned non-zero exit status 1
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for quil
Failed to build quil
ERROR: ERROR: Failed to build installable wheels for some pyproject.toml based projects (quil)
Not working ...

Monday, November 25, 2024

Fedora 41 : assembly fasm with cat command and qemu test.

I tested the basic direct binary concatenation where the second file is appended to the end of the first file, preserving all bytes exactly as they are without any linking metadata or relocations with the cat command.
Let's see how can do this.
You need to create two files: kernel.fasm and kernel2.fasm.
First will like this:
org 7C00h

; First stage bootloader
start:
    mov [bootdrive], dl
    mov ax, 0x2000  ; Load kernel at 0x2000:0
    mov es, ax
    xor bx, bx      ; ES:BX = buffer
    
    mov ah, 02h     ; Read sectors
    mov al, 6       ; Number of sectors to read
    mov ch, 0       ; Cylinder 0
    mov cl, 2       ; Start from sector 2
    mov dh, 0       ; Head 0
    mov dl, [bootdrive]
    int 13h
    
    jmp 0x2000:0    ; Jump to second stage

bootdrive db 0
times 510-($-$$) db 0
dw 0xAA55
The second one named kernel2.fasm will come with new features:
org 0

COLS equ 80
ROWS equ 25
VIDEO_MEM equ 0xB800

; Box drawing characters
BOX_DR    equ 201  ; ╔
BOX_HL    equ 205  ; ═
BOX_DL    equ 187  ; ╗
BOX_VL    equ 186  ; ║
BOX_UR    equ 200  ; ╚
BOX_UL    equ 188  ; ╝
BOX_BLOCK equ 219  ; █
...
Use fasm and cat commands to create the bin files and the result file for qemu:
mythcat@localhost:~/fasm$ ./fasm.x64 kernel.fasm kernel.bin
flat assembler  version 1.73.32  (16384 kilobytes memory, x64)
2 passes, 512 bytes.
mythcat@localhost:~/fasm$ ./fasm.x64 kernel2.fasm kernel2.bin
flat assembler  version 1.73.32  (16384 kilobytes memory, x64)
2 passes, 132 bytes.
mythcat@localhost:~/fasm$ cat kernel.bin kernel2.bin > os.img
The last step is to run qemu-system-i386 to test the result
mythcat@localhost:~/fasm$ qemu-system-i386 -fda os.img
The result is this:

Sunday, November 24, 2024

Fedora 41 : Error gio default application for image.

I run today an old python script in my Fedora 41 to create an image and the result after running was:
mythcat@localhost:~$ python test_001.py 
gio: file:///tmp/tmpwy1k4wyo.PNG: Failed to find default application for content type ‘image/png’
This can be fixed with an default application for images like feh:
$ sudo dnf5 install feh

Fedora 41 : remove package lead to unexpected results.

Today I saw the mutter package from Fedora distro come with a new update.
I don't use this package, ... is a mess in my oppinion.
But I found this output when I tried to remove
root@localhost:/home/mythcat# dnf5 remove mutter 
Package                          Arch   Version                 Reposit      Size
Removing:
 mutter                          x86_64 47.1-3.fc41             updates  12.5 MiB
Removing dependent packages:
 gdm                             x86_64 1:47.0-8.fc41           updates   5.3 MiB
 gnome-shell                     x86_64 47.1-1.fc41             updates  13.8 MiB
Removing unused dependencies:
 accountsservice                 x86_64 23.13.9-5.fc41          fedora  379.5 KiB
 accountsservice-libs            x86_64 23.13.9-5.fc41          fedora  212.3 KiB
 bluez-obexd                     x86_64 5.79-1.fc41             updates 345.1 KiB
 bolt                            x86_64 0.9.8-3.fc41            fedora  503.3 KiB
 boost-thread                    x86_64 1.83.0-8.fc41           fedora  136.8 KiB
 color-filesystem                noarch 1-34.fc41               fedora  151.0   B
 colord                          x86_64 1.4.7-5.fc41            fedora    1.7 MiB
 colord-gtk4                     x86_64 0.3.1-2.fc41            fedora   35.6 KiB
 composefs-libs                  x86_64 1.0.6-1.fc41            fedora  166.3 KiB
 cups-pk-helper                  x86_64 0.2.7-8.fc41            fedora  379.0 KiB
 dbus-daemon                     x86_64 1:1.14.10-4.fc41        fedora  553.2 KiB
 evolution-data-server           x86_64 3.54.1-1.fc41           updates   8.8 MiB
 evolution-data-server-langpacks noarch 3.54.1-1.fc41           updates   8.8 MiB
 flatpak-libs                    x86_64 1.15.10-1.fc41          fedora    1.0 MiB
 ...
 
Is this ok [y/N]: N
 ... 
If I used this command the gdm and gnome-shell is gone:
root@localhost:/home/mythcat# dnf5 remove --noautoremove mutter 
Package                        Arch   Version                   Repository        Size
Removing:
 mutter                        x86_64 47.1-3.fc41               updates       12.5 MiB
Removing dependent packages:
 gdm                           x86_64 1:47.0-8.fc41             updates        5.3 MiB
 gnome-session-wayland-session x86_64 47.0.1-1.fc41             fedora        15.9 KiB
 gnome-shell                   x86_64 47.1-1.fc41               updates       13.8 MiB

Transaction Summary:
 Removing:           4 packages
 
Is this ok [y/N]: N
...
I tried with the --noautoremove, and --exclude args and not result:
root@localhost:/home/mythcat# dnf5 remove --noautoremove --exclude=gnome-shell,gdm mutter
Failed to resolve the transaction:
Problem: installed package gnome-shell-47.1-1.fc41.x86_64 requires libmutter-15.so.0()(64bit), but none of the providers can be installed
  - installed package gnome-shell-47.1-1.fc41.x86_64 requires libmutter-clutter-15.so.0()(64bit), but none of the providers can be installed
  - installed package gnome-shell-47.1-1.fc41.x86_64 requires libmutter-cogl-15.so.0()(64bit), but none of the providers can be installed
  - installed package gnome-shell-47.1-1.fc41.x86_64 requires libmutter-mtk-15.so.0()(64bit), but none of the providers can be installed
  - installed package gnome-shell-47.1-1.fc41.x86_64 requires mutter(x86-64) >= 47.0, but none of the providers can be installed
  - conflicting requests
  - problem with installed package
The solution is old rpm tool:
root@localhost:/home/mythcat# rpm -e --nodeps mutter 
root@localhost:/home/mythcat# dnf5 remove mutter
No packages to remove for argument: mutter

Nothing to do.

Wednesday, November 20, 2024

Fedora 41 : generated shapes with python !

Today I created this source code in python that generates eight random convex polygons. The idea was to create sprites for a 2D game: snowballs, boulders, or similar objects ... Obviously I also used Sonet 3.5 artificial intelligence. You can find the source code on the pagure account in fedora.
#!/usr/bin/env python3
"""
SVG Polygon Generator

This script generates multiple deformed polygonal shapes and saves them as separate SVG files.
Each polygon maintains convex properties while having controlled random deformations.

Features:
    - Generates 8 unique polygonal shapes
    - Controls deformation through radial and angular factors
    - Maintains convex properties
    - Exports each shape to a separate SVG file
    - Uses random colors for visual distinction

Usage:
    python generate_svgs.py

Output:
    Creates 8 SVG files named 'polygon_1.svg' through 'polygon_8.svg'
"""

from lxml import etree
import random
import math
from pathlib import Path


def create_svg_root():
    """Create and return a base SVG root element with standard attributes."""
    root = etree.Element("svg")
    root.set("width", "500")
    root.set("height", "500")
    root.set("xmlns", "http://www.w3.org/2000/svg")
    return root


def calculate_points(center_x: float, center_y: float, radius: float, 
                    num_sides: int, deform_factor: float) -> list:
    """
    Calculate polygon points with controlled deformation.

    Args:
        center_x: X coordinate of polygon center
        center_y: Y coordinate of polygon center
        radius: Base radius of the polygon
        num_sides: Number of polygon sides
        deform_factor: Maximum allowed deformation factor

    Returns:
        List of tuples containing (x, y) coordinates
    """
    points = []
    angle_step = 2 * math.pi / num_sides
    
    for i in range(num_sides):
        angle = i * angle_step
        radial_deform = random.uniform(-deform_factor, deform_factor)
        angular_deform = random.uniform(-deform_factor/2, deform_factor/2)
        
        modified_angle = angle + angular_deform
        modified_radius = radius * (1 + radial_deform)
        
        x = center_x + modified_radius * math.cos(modified_angle)
        y = center_y + modified_radius * math.sin(modified_angle)
        points.append((x, y))
    
    return points


def generate_deformed_shapes():
    """Generate multiple deformed polygons and save them to separate SVG files."""
    # Base parameters
    num_sides = 8
    center_x = 250
    center_y = 250
    base_radius = 150
    max_deformation = 0.15
    output_dir = Path("generated_polygons")
    
    # Create output directory if it doesn't exist
    output_dir.mkdir(exist_ok=True)

    for i in range(8):
        root = create_svg_root()
        points = calculate_points(center_x, center_y, base_radius, 
                                num_sides, max_deformation)
        
        path = etree.SubElement(root, "path")
        path_data = f"M {points[0][0]} {points[0][1]}"
        path_data += "".join(f" L {p[0]} {p[1]}" for p in points[1:])
        path_data += " Z"
        
        path.set("d", path_data)
        path.set("fill", "none")
        path.set("stroke", f"#{random.randint(0, 16777215):06X}")
        path.set("stroke-width", "2")
        path.set("opacity", "0.7")

        # Save individual SVG file
        output_file = output_dir / f"polygon_{i+1}.svg"
        tree = etree.ElementTree(root)
        tree.write(str(output_file), pretty_print=True, 
                  xml_declaration=True, encoding='utf-8')
    
    print(f"Generated {num_sides} polygons in {output_dir}")

if __name__ == "__main__":
    generate_deformed_shapes()

Saturday, November 16, 2024

Fedora 41 : Bookworm - open-source eBook reader !

Bookworm is an open-source eBook reader with an easy and simple layout supporting different file formats like epub, pdf, mobi, cbr and cbz. See the official webpage.
root@localhost:/home/mythcat# dnf5 search bookworm
Updating and loading repositories:
Repositories loaded.
Matched fields: name (exact)
 bookworm.x86_64: Simple, focused eBook reader
root@localhost:/home/mythcat# dnf5 install bookworm.x86_64
Updating and loading repositories:
Repositories loaded.
Package                 Arch   Version                      Repository      Size
Installing:
 bookworm               x86_64 1.1.3-0.13.20200414git.c7c36 fedora       3.6 MiB
Installing dependencies:
 granite                x86_64 6.2.0-9.fc41                 fedora     949.2 KiB
 javascriptcoregtk4.0   x86_64 2.46.3-1.fc41                updates     28.3 MiB
 webkit2gtk4.0          x86_64 2.46.3-1.fc41                updates     75.4 MiB

Transaction Summary:
 Installing:         4 packages
...
[6/6] Installing bookworm-0:1.1.3-0.13. 100% | 430.8 KiB/s |   3.7 MiB |  00m09s
Complete!ng trigger-install scriptlet: hicolor-icon-theme-0:0.17-19.fc41.noarch
The last step, search this on gnome environmet and you will find this application.