0

I am somehow new to python and would like to get rid of use of for loop as it makes the program very slow. So I would like vectorize it. however, I am not sure how would I use it with two conditions Here I shared the code and would like someone could hit me..

image to be imported


import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def nothing(color):
    pass
cv.namedWindow('Trackbar ')
cv.resizeWindow('Trackbar',800,200)
cv.createTrackbar('v_min','Trackbar ',0,255,nothing)
cv.createTrackbar('v_max','Trackbar ',0,255,nothing)
while True:
    img_org = cv.imread('sat.png',cv.IMREAD_COLOR)
    img_np=np.array(img_org)
    img2_np=np.array(img_org)
    value_min=cv.getTrackbarPos('v_min','Trackbar ')
    value_max=cv.getTrackbarPos('v_max','Trackbar ')
    height, width = img_np.shape[:2]
    for j in range (0,width) :
        for i in range (0,height):
            for k in range (0,2):
                if (value_min < img_np[i,j,k] <= value_max):
                    img2_np[i,j] = ((img_np[i,j]-value_min)/(value_max-value_min))*255 
                else:
                    img2_np[i,j] = 0
#Here is the vectorization to remove all for and if loop (what I think )
 #   img_np[(value_min < img_np.any() <= value_max)]=((img_np-value_min)/(value_max-value_min))*255 |       img_np[(value_min > img_np.any() >= value_max)]=0
    cv.imshow('FINAL IMAGE',img_np)
    cv.imshow('image',img_org)
    kk = cv.waitKey(1000) & 0xFF # large wait time to remove freezing
    if kk == 113 or kk == 27:
        break

I am expecting to convert or I mean to remove all (for, if/else loop) with vectorize so less time.. so I tried first to with one if condition and seems working however, when I tried to add with two conditions like (|) else seems not working

4
  • 1
    You're code's not quite making sense. Did you mean do you img2_np[i][j][k] in your if/else statement? I'm not quite sure what you want to happen when k=0 gives you the if side and k=1 gives you the else side. (ps. In numpy, it's equivalent and more conventional to write img2_np[i, j, k] wheni, j, and k are integers. Commented Nov 21, 2024 at 1:19
  • @FrankYellin Thanks for your comment, I edited the indices to be written directly [i,j,k].. Well, the third index is not a big deal for me.. i mean I just would like to replace each element ((img_np[i][j]-value_min)/(value_max-value_min))*255 when if condition exists, otherwise replace with zeros It is working with for and if loop but super slow so kindly asking to how to vectorize it. Let me know Thanks
    – Momo R
    Commented Nov 21, 2024 at 10:57
  • Please provide a minimal reproducible example that we can run. That is, a little code to generate a realistic array (can be random but should otherwise be like your real ones) and min/max, and then the transformation code. Don't load a file we don't have, and get rid of the cv stuff and user interaction that aren't relevant to the question.
    – no comment
    Commented Nov 21, 2024 at 11:35
  • What's the point of the k=0 iteration if the k=1 iteration overwrites img2_np[i,j] anyway?
    – no comment
    Commented Nov 21, 2024 at 11:58

2 Answers 2

0

Your code using for loops in that order is the worst possible way you could do it unless the images being processed have width, height of 2 or less.

for j in range (0,width) :
    for i in range (0,height):
        for k in range (0,2):
            if (value_min < img_np[i][j][k] <= value_max):
                img2_np[i][j] = ((img_np[i][j]-value_min)/(value_max-value_min))*255 
            else:
                img2_np[i][j] = 0

The innermost loop has maximal setup overheads that way around.

It should be replaced by

for k in range (0,2):
    for j in range (0,width) :
        for i in range (0,height):
            if (value_min < img_np[i][j][k] <= value_max):
                img2_np[i][j][k] = ((img_np[i][j][k]-value_min)/(value_max-value_min))*255 
            else:
                img2_np[i][j][k] = 0

I don't know how good the optimiser is in Python at removing loop invariants and strength reduction so it might also be worth manually moving the scaling out of the loop and multiplying rather than dividing. Division is the most expensive binary arithmetic operation ~10x slower than +,-,*.

     scalefactor = 255/(value_max-value_min)
     ...
     if (value_min < img_np[i][j][k] <= value_max):
                img2_np[i][j][k] = ((img_np[i][j][k]-value_min)*scalefactor
            else:
                img2_np[i][j][k] = 0

You also need to check the memory allocation rules for 3D arrays in Python to ensure you are making sequential memory access if the arrays are large.

4
  • Thanks for your comment, I tried and seem very slow yet.. Actually wondering is there is a way to replace each element of array based on If condition using vectorization.. something even like np.where ?
    – Momo R
    Commented Nov 21, 2024 at 11:10
  • 1
    This answer would have made perfect sense if we had been dealing with C or Fortran, but this is Python. The standard Python implementation is staggeringly inefficient. Before it gets down to executing that DIV or MUL instruction, it will have spent thousands of instructions just to figure out what to do. And that’s for each turn through the loop! Similarly, any gains from better array ordering are probably completely swamped by the general inefficiency of the interpreter. Commented Nov 21, 2024 at 11:22
  • I actually use Julia when I want to do things that are tedious in C or Fortran. I had assumed perhaps naively that similar JIT compilation of hotspots applied to Python too. Commented Nov 21, 2024 at 11:53
  • Python doesn't do any jit. numpy is a class that gains its speed by using its own whole-array compiled methods.
    – hpaulj
    Commented Nov 21, 2024 at 16:04
0

Without actually testing this, this change should work - assuming in 1 condition more or less works.

img_np2 = np.zeros_like(img_np2) # default 0s
cond = (value_min < img_np) & (img_np <= value_max)
img_np2[cond] = ((img_np[cond]-value_min)/(value_max-value_min))*255

alternatively

img_np2 = np.where(cond, ((img_np-value_min)/(value_max-value_min))*255, 0)

a<x<=b only works with scalar x. for array, it needs to be separated into two clauses, ()&().

The where version evaluates at all points, but that should be ok here. Sometimes we want conditional evaluation to avoid things like run time errors, divide by zero, etc. Then we need to use the first version, or use the where clause in a ufunc (like np.divide).

1
  • I think the parentheses are wrong in the np.where version.
    – Barmar
    Commented Nov 21, 2024 at 23:57

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.