Lenovo Legion 5 15ACH6H Ubuntu – All working :)

Yesterday I got an nice new laptop. But getting it up and running was a bit of a PITA. So, I’ll just write down what I did to make it work.

What works?

  • HDMI multi monitor
  • WiFi
  • Sound
  • Touchpad
  • Brightness control
  • Webcam (+HW cam switch)

What did I do:

  • Plugin a cable for networking
  • Install Ubuntu 21.04, regular install, all updates, LUKS encypted disc
  • After install, update everything again.

Then, I installed a new mainline kernel using mainline

https://github.com/bkw777/mainline

After installing that, I opted for the 5.13.14-051314-generic kernel.

Then, after rebooting, I installed the nvidia driver by using ubuntu’s additional drivers. I used version 470.

Mon Sep 6 15:58:51 2021
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 470.57.02 Driver Version: 470.57.02 CUDA Version: 11.4 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 NVIDIA GeForce … Off | 00000000:01:00.0 On | N/A |
| N/A 37C P8 10W / N/A | 541MiB / 5946MiB | 6% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+

And again, reboot.

This worked excellent. Then I went on installing the wifi driver from here:

https://github.com/lwfinger/rtw89

After building, added the rtw89pci to the /etc/modules to make it survive a reboot.

Rebooted to make sure it al is reboot safe.

Then, I plugged in my Philips 27″ 275B external monitor and it got recognized. I could move the position using the Ubuntu -> displays menu.

Result:


Yay 🙂

Replacing the background of keycloak login screen in docker / docker-compose

So. You want to replace the default background of the login-screen in a docker-image in docker-compose?

Just mount a new image on the existing background 😉

keycloak:
image: quay.io/keycloak/keycloak:13.0.0
environment:
- KEYCLOAK_USER=admin
- KEYCLOAK_PASSWORD=superdupersecret
- DB_VENDOR=h2
- PROXY_ADDRESS_FORWARDING=true
volumes:
- ./keycloak-data:/opt/jboss/keycloak/standalone/data
- ./my-custom-image.png:/opt/jboss/keycloak/themes/keycloak/login/resources/img/keycloak-bg.png

MS teams background replacement Linux – Python

Hi all,

I use Ubuntu linux exclusively; but all my colleagues have the fancy-background-replacement in MS teams; and I wanted that as well 🙂

So, I coded it up! Basically, I route my webcam throught an OpenCV/Tesseract python script (using Bodypix) and dump the rendered frames to a fake webcam. Result:

 

Here is how I did it:

Install v4l2loopback

sudo apt install v4l2loopback-dkms

Install pip3

sudo apt install python3-pip

Install pyfakewebcam

https://github.com/jremmons/pyfakewebcam

Install OpenCV and imutils

pip3 install opencv

pip3 install imutils

Install bodypix

https://github.com/ajaichemmanam/simple_bodypix_python

Get the mobile tensorflow model

From the simple_bodypix_python use the get_model to install the

./get-model.sh bodypix/mobilenet_float_050/model-stride16

Start the script

Copy the script below to the simple_bodypix_python directory and run it. You can check if it works by for example opening /dev/video2 in VLC


#
# Author: Rob Audenaerde – License: Apache 2.0
#
# Heavily inspired by the code by: ajaichemmanam@gmail.com, found here: https://github.com/ajaichemmanam/simple_bodypix_python
#
import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import math
import time
import cv2
import pyfakewebcam
from PIL import Image
from PIL import ImageFilter
from utils import load_graph_model, get_input_tensors, get_output_tensors
import tensorflow as tf
# make tensorflow stop spamming messages
os.environ['TF_CPP_MIN_LOG_LEVEL'] = "3"
# SETUP video devices.
# Make sure the numbers are correct (/dev/video2 is my loopback device)
camera = pyfakewebcam.FakeWebcam('/dev/video2', 640, 480)
cap = cv2.VideoCapture(0)
# Setup the backgroud
bg = cv2.imread('matrix.jpg')
# PATHS
modelPath = 'bodypix_mobilenet_float_050_model-stride16'
# CONSTANTS
OutputStride = 16
print("Loading model…", end="")
graph = load_graph_model(modelPath) # downloaded from the link above
print("done.\nLoading sample image…", end="")
# Get input and output tensors
input_tensor_names = get_input_tensors(graph)
output_tensor_names = get_output_tensors(graph)
input_tensor = graph.get_tensor_by_name(input_tensor_names[0])
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
# evaluate the loaded model directly
sess = tf.compat.v1.Session(graph=graph)
# Resize the background, and preprocess it to 32f.
bg_resized = cv2.resize(bg, (640,480), interpolation = cv2.INTER_AREA)
bg_resized_32f = np.float32(cv2.cvtColor(bg_resized, cv2.COLOR_BGR2RGB))
# Forever looping 🙂
while True:
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = Image.fromarray(frame)
imgWidth, imgHeight = img.size
targetWidth = (int(imgWidth) // OutputStride) * OutputStride
targetHeight = (int(imgHeight) // OutputStride) * OutputStride
img = img.resize((targetWidth, targetHeight))
x = tf.keras.preprocessing.image.img_to_array(img, dtype=np.float32)
InputImageShape = x.shape
widthResolution = int((InputImageShape[1] – 1) / OutputStride)
heightResolution = int((InputImageShape[0] – 1) / OutputStride)
#mobile net preprocessing
x = (x/127.5)-1
sample_image = x[tf.newaxis, …]
output_tensor_names = ['float_segments:0']
results = sess.run(output_tensor_names, feed_dict={input_tensor: sample_image})
segments = np.squeeze(results[0], 0)
# Segmentation MASk
segmentation_threshold = 0.7
segmentScores = tf.sigmoid(segments)
mask = tf.math.greater(segmentScores, tf.constant(segmentation_threshold))
segmentationMask = tf.dtypes.cast(mask, tf.int32)
segmentationMask = np.reshape(
segmentationMask, (segmentationMask.shape[0], segmentationMask.shape[1]))
# Create the mask image
mask_img = Image.fromarray(segmentationMask * 255)
mask_img = mask_img.resize(
(targetWidth, targetHeight), Image.LANCZOS).convert("RGB")
# Convert the segmentation mask to GRAY
proc_out = cv2.cvtColor(np.asarray(mask_img), cv2.COLOR_RGB2GRAY)
#Blur to smoothen the blocky input
proc_out = cv2.GaussianBlur(proc_out,(151,151),0)
#Threshold and blur to reduce the part that gets blended
a, proc_out = cv2.threshold(proc_out,127,255,cv2.THRESH_BINARY)
proc_out = cv2.GaussianBlur(proc_out,(11,11),0)
#Convert back to RGB and float32 for blending
proc_out = cv2.cvtColor(proc_out, cv2.COLOR_GRAY2RGB)
mask_32f = np.float32(proc_out) / 255.0
mask_32f_inv = 1.0 – mask_32f
#blend
img_in = np.array(img)
img_in_32f = np.float32(img_in)
img_fg = cv2.multiply(mask_32f , img_in_32f , 1.0/255.0)
img_bg = cv2.multiply(mask_32f_inv, bg_resized_32f, 1.0/255.0)
#add
img_out = cv2.add(img_fg, img_bg);
#convert back to 3channel 8 bit
img_out = np.uint8(img_out)
#put it in the fake webcam
camera.schedule_frame(img_out)
#Sleep a bit to keep the CPU usages acceptable for other stuff 🙂
time.sleep(1/30.0)

 

Open Data Explorations – Lucene based BI

Hi all,

After a few years of silence on the blogosphere I deciced to write up a little on how my company ValueCare is currently building could-based, search-based open BI solutions to display open data.

Be sure to take a look at some of the sites; for example: https://corona.searchdata.com

Screenshot

Neat huh. Some quick info: visualisations are made using HighCharts and custom maps based on data from the cbs. Front-end code is Wicket, back-end is java and powered by the excellent java-based Lucene search engine.

Leveraging lucene-facets for pivot-tables

This solution will run up to 100 million indexed data-rows on a regular sized server; because we use a big lucene index we are able to create pivot tables over all kinds of different sorts of data, as long as they share one or more similar columns. The can use the power of lucene search engine to filter these rows on all kinds of queries.

To do this efficently on all kinds of data without any prior knowlegde of this data, we use Lucene TaxonomyFacets and AssociatedFacetFields. Basically, each unique value will be assigned an ordinal and this ordinal is stored in a field-name-based BinaryDocValues field.

 

Java’s String.format() in JavaScript

For a recent project I needed to use Java’s excellent String.format() in JavaScript. I couldn’t find a implementation that did this, so I decided to try and port it from the OpenJDK sources. In case you are interested, please see my github project: https://github.com/RobAu/javascript.string.format

Currently, all formatting flags are supported. Conversion from integer, long, double and all kinds of dates are implemented. Not implemented is conversion to scientific format, hexadecimal and some other stuff, although it is quite straight-forward to implement the latter.

 

Override image src in css

There is a solution that I found out today (works in IE6+, FF, Opera):

<img src=”tiger.png” style=”width:0px; height:0px; padding: 50px; background: url(butterfly.png);”>

How it works:

– The image is shrunk until no longer visible by the width & height.
– Then, you need to ‘reset’ the image size; there I use padding (this
one gives a 16×16 image, of course you can use padding-left /
padding-top to make rectangular images)
– Finally, the new image is put there using background

It also works for submit-input-images, they stay clickable.

See: http://www.audenaerde.org/csstricks.html

Enjoy!

Unobtrusive table column resize with jquery

With this piece of code, simple add the class  ‘resizabletable’ to your table and the columns can be easily resized by drag and drop!

I was inspired by the flexigrid, but wanted a simpler solution. This one also nicely integrates into Wicket DataTable, for example.

Update: fixed the multi table on a page bug, kindly pointed out by Nick (see comments)

To see it working: http://www.audenaerde.org/example.html

<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript"
        src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript"
        src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.js"></script>
    <script type="text/javascript">
    function resetTableSizes (table, change, columnIndex)
    {
        //calculate new width;
        var tableId = table.attr('id');
        var myWidth = $('#'+tableId+' TR TH').get(columnIndex).offsetWidth;
        var newWidth = (myWidth+change)+'px';

        $('#'+tableId+' TR').each(function()
        {
            $(this).find('TD').eq(columnIndex).css('width',newWidth);
            $(this).find('TH').eq(columnIndex).css('width',newWidth);
        });
        resetSliderPositions(table);
    };

    function resetSliderPositions(table)
    {
        var tableId = table.attr('id');
        //put all sliders on the correct position
        table.find(' TR:first TH').each(function(index)
        {
            var td = $(this);
            var newSliderPosition = td.offset().left+td.outerWidth() ;
            $("#"+tableId+"_id"+(index+1)).css({  left:   newSliderPosition , height: table.height() + 'px'}  );
        });
    }

    function makeResizable(table)
    {        
        //get number of columns
        var numberOfColumns = table.find('TR:first TH').size();

        //id is needed to create id's for the draghandles
        var tableId = table.attr('id');
        for (var i=0; i<=numberOfColumns; i++)
        {
            //enjoy this nice chain :)
            $('<div id="'+tableId+'_id'+i+'"></div>').insertBefore(table).data('tableid', tableId).data('myindex',i).draggable(
            { axis: "x",
              start: function () 
              {
                var tableId = ($(this).data('tableid'));
                $(this).toggleClass( "dragged" );
                //set the height of the draghandle to the current height of the table, to get the vertical ruler
                $(this).css({ height: $('#'+tableId).height() + 'px'} );
              },
              stop: function (event, ui) 
              {
                var tableId = ($(this).data('tableid'));
                $( this ).toggleClass( "dragged" ); 
                var oldPos  = ($( this ).data("draggable").originalPosition.left);
                var newPos = ui.position.left;
                var index =  $(this).data("myindex");
                resetTableSizes($('#'+tableId), newPos-oldPos, index-1);
              }          
            }
            );;
        };

        resetSliderPositions(table);

    }
    $(document).ready(function()
    {
        $(".resizabletable").each(function(index)
        {
            makeResizable($(this));
        });

    });

    </script>
    <style>
        .draghandle.dragged
        {
            border-left: 1px solid #333;
        }

        .draghandle
        {
            position: absolute;
            z-index:5;
            width:5px;
            cursor:e-resize;
        }
        TH
        {
            border-left: 1px solid #333;
            border-bottom: 1px solid #333;
        }
    </style>
</head>
<body>
<div id="container" style="border: 1px solid blue;">
    <table id="mytable" style="width:100%; border: 1px solid red;" border="0" cellspacing="0" cellpadding="0" >
    <thead>
        <tr>
            <th style="">Dit is een interessante zin!</th>
            <th style="">b</th>
            <th style="">b</th>
            <th style="">b</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td style="">Dit is een interessante zin!</td>
            <td style="">b</td>
            <td style="">b</td>
            <td style="">b</td>
        </tr>
        <tr>
            <td style="">b</td>
            <td style="">a</td>
            <td style="">b</td>
            <td style="">b</td>
            </tr>
        <tr>
            <td style="">b</td>
            <td style="">a</td>
            <td style="">b</td>
            <td style="">b</td>
        </tr>
    </tbody>
    </table>
</div>
</body>
</html>

Enjoy :)

Master Thesis!

I couldn’t find my master thesis fast enough in my gmail, so I post it here, for the world to see! (yeah it’s version 12..)

Final_report_00012

Align Two DIVS on vertical middle

Ha,

Today I found an elegant CSS way of aligning two DIVs vertically on their centers. After spending some search on Google and not finding a satisfactionary solution, I treid to create my own. Here it is:

<span>
 <div style="display:inline-block; vertical-align:middle">
 RobAu's <br>Vertically aligned DIVS
 </div>
 <div style="display:inline-block; vertical-align:middle;width:100px;">
 <div style="width:100px; height:150px; background-color:red"></div>
 <div style="width:100px; height:150px; background-color:blue"></div>
 </div>
</span>

The span is used to have an normal line. The divs are converted to inline-blocks, like images. That way, they can be aligned in the middle.By simply putting two container DIVs in the span, they are alinged vertically, without giving any pre-set heights, percentages, positioning etc.

Feel free to use!

RobAu’s
Vertically aligned DIVS

Lenovo SL500 and Ubuntu.. fixing the loose ends

Half a year ago I started working for a new company and they provided me with a Lenovo SL 500. I started using Ubuntu a year ago, so I was eager to try it out on my new laptop. I wanted to keep the pre-installed Windows intact, so I went the wubi way  (http://wubi-installer.org/).

After installing most things seem to work fine. Except for a few things. The extra keys, the birghtness and wifi where working non optimal. So i started my search on the internet and found solutions to all of my problems!

Setting the brightness

Ok. So setting the brightness was not easy. It requires a installation of extra drivers, and of the 2.6.30 kernel.

Installation of the new kernel (and new nVidia drivers)

http://law-and-tech.blogspot.com/2009/06/thinkpad-sl500-and-linux.html

Installation of the SL500 acpi driver

http://ubuntuforums.org/showpost.php?p=7483027&postcount=4

Installation of the Wifi firmware

Well. I’m not sure if it really fixes the drop-outs of the connections, but there is no more error in the kernel.log

http://intellinuxwireless.org/?n=downloads

Look fot the 5100 agn. Unzip the zip and copy the .ucode file to the /lib/firmware/<your_kernel>