I'm trying to develop a Pivot Points indicator on Trading View v4.0 and an issue I'm facing is that the pivot lines are drawn only up to the current bar. The behavior I want is that I want the line to extend for the duration of the period. For example, the weekly pivot line should extend from Monday to Sunday even if today is Monday.
Below is the code I'm using as well as the result:
// Function outputs 1 when it's the first bar of the D/W/M/Y
is_newbar(res) =>
ch = 0
if(res == 'Y')
t = year(time('D'))
ch := change(t) != 0 ? 1 : 0
else
t = time(res)
ch := change(t) != 0 ? 1 : 0
ch
bars_since_week = 0
bars_since_week := is_newbar(pp_res_week) ? 0 : bars_since_week[1] + 1
vpp_p_week = line.new(bar_index[min(bars_since_week, 300)], PP_week, bar_index, PP_week, color=color.black, width = 2, style = line.style_solid, extend = extend.none)
vs1_p_week = line.new(bar_index[min(bars_since_week, 300)], S1_week, bar_index, S1_week, color=color.black, width = 2, style = line.style_solid, extend = extend.none)
vs2_p_week = line.new(bar_index[min(bars_since_week, 300)], S2_week, bar_index, S2_week, color=color.black, width = 2, style = line.style_solid, extend = extend.none)
vs3_p_week = line.new(bar_index[min(bars_since_week, 300)], S3_week, bar_index, S3_week, color=color.black, width = 2, style = line.style_solid, extend = extend.none)
vr1_p_week = line.new(bar_index[min(bars_since_week, 300)], R1_week, bar_index, R1_week, color=color.black, width = 2, style = line.style_solid, extend = extend.none)
vr2_p_week = line.new(bar_index[min(bars_since_week, 300)], R2_week, bar_index, R2_week, color=color.black, width = 2, style = line.style_solid, extend = extend.none)
vr3_p_week = line.new(bar_index[min(bars_since_week, 300)], R3_week, bar_index, R3_week, color=color.black, width = 2, style = line.style_solid, extend = extend.none)
Also, below is the behavior I would expect - notice the longer lines:
It's not a simple matter, at least not the way it's done here ) There is perhaps a better way, but haven't figured out any:
You'll need to work with xloc = xloc.bar_time to be able to draw a line in the future when you need to.
Use the f_avgDilationOf() from the PineCoders MTF Selection Framework to know the average number of chart bars in the dilation of the higher TF (on a 24/7 market, this would be 7 on a daily chart if HTF=1W).
Figure out if current chart bars (current being the one the script is executing on) are included in the last dilation of the HTF. We need this info because when that is true, we will project the line in the future.
//#version=4
study("Periodic lines", "", true)
htf = input("W", type = input.resolution)
// Returns the average number of current chart bars in the given target HTF resolution (this reflects the dataset's history).
f_avgDilationOf(_res) =>
// _res: resolution of any TF (in "timeframe.period" string format).
b = barssince(change(time(_res)))
cumTotal = cum(b == 0 ? b[1] + 1 : 0)
cumCount = cum(b == 0 ? 1 : 0)
cumTotal / cumCount
// Period change detection.
pChange(res) =>
change(time(res == 'Y' ? 'D' : res))
// Get some previous value from last HTF period.
pHi = security(syminfo.tickerid, htf, high[1], lookahead = barmerge.lookahead_on)
// Verify if current charts bars are part of the last dilation of HTF.
lastPBar = security(syminfo.tickerid, htf, barstate.islast, lookahead = barmerge.lookahead_on)
// Get avg no of chart bars in one dilation of HTF.
dilation = round(f_avgDilationOf(htf))
timeDelta = time - time[1]
var line pHiLine = na
// Holds bar index where a new line is created.
var pHiBar = 0
if pChange(htf)
// Extend old line for the last bar before creating a new one.
line.set_xy2(pHiLine, time, pHi[1])
// Save bar index on transition.
pHiBar := bar_index
// Create new line.
pHiLine := line.new(time, pHi, time + timeDelta, pHi, xloc.bar_time, color = color.black, width = 2)
// Make type of the 2 `if` blocks the same.
float(na)
else
// We are not on a transition; prolong line until next transition.
line.set_xy2(pHiLine, time, pHi)
float(na)
// If we are in the last bars of the HTF resolution's dilation, project line into the future with remaining bars in average no of bars in dilation.
if lastPBar
line.set_xy2(pHiLine, time + (timeDelta * (dilation - (bar_index - pHiBar))), pHi)
Caveat: Haven't forward-tested this, so unsure how it'll perform in those conditions.
Related
I want to create interactive line- and topoplot depending on menu. I figured out how to make red the line chosen in menu, but it doesn't work for topoplot marks (black circles inside topoplot). I can change it manually (cmap[][4] = RGB{N0f8}(1.0,0.0,0.0)), but how to do that interactively?
f = Figure(backgroundcolor = RGBf(0.98, 0.98, 0.98), resolution = (1500, 700))
ax = Axis(f[1:3, 1], xlabel = "Time [s]", ylabel = "Voltage amplitude [µV]")
N = 1:length(pos) #1:4
hidespines!(ax, :t, :r)
GLMakie.xlims!(-0.3, 1.2)
hlines!(0, color = :gray, linewidth = 1)
vlines!(0, color = :gray, linewidth = 1)
times = range(-0.3, length=size(dat_e,2), step=1 ./ 128)
lines = Dict()
for i in N
mean_trial = mean(dat_e[i,:,:],dims=2)[:,1]
line = lines!(times, mean_trial, color = "black")
lines[i] = line
end
hidedecorations!(ax, label = false, ticks = false, ticklabels = false)
topo_axis = Axis(f[2, 2], width = 178, height = 178, aspect = DataAspect())
Makie.xlims!(low = -0.2, high = 1.2)
Makie.ylims!(low = -0.2, high = 1.2)
topoMatrix = eegHeadMatrix(pos[N], (0.5, 0.5), 0.5)
cmap = Observable(collect(ColorScheme(range(colorant"black", colorant"black", length=30))))
#cmap[][4] = RGB{N0f8}(1.0,0.0,0.0)
topo = eeg_topoplot!(topo_axis, N, # averaging all trial of 30 participants on Xth msec
raw.ch_names[1:30];
positions=pos, # produced automatically from ch_names
interpolation=NullInterpolator(),
enlarge=1,
#colorrange = (0, 1), # add the 0 for the white-first color
colormap = cmap[],
label_text=false)
hidedecorations!(current_axis())
hidespines!(current_axis())
num_prev = 0
menu = Menu(f[3, 2], options = raw.ch_names[1:30], default = nothing)#, default = "second")
on(menu.selection) do selected
if selected != nothing
num = findall(x->x==menu.selection[], raw.ch_names[1:30])[]
if num_prev != 0
lines[num_prev].color = "black"
cmap[][num] = RGB{N0f8}(1.0,0.0,0.0)
end
lines[num].color = "red"
cmap[][num] = RGB{N0f8}(1.0,0.0,0.0)
num_prev = num
end
end
notify(menu.selection)
#print(cmap[])
f
We solved this by putting this string at the end of the menu.selection section:
notify(lines)
It works, because lines() automatically creates Observable.
I have two questions about the Gekko library in Python.
Is there any way to increase the below code's performance (Input1, Input2)? The code was solved fast, and the results were correct when lines 170~195 were excluded (commented). However, when I executed the entire code (including lines 170-195), the performance decreased dramatically, and I didn't obtain the results after waiting more than 30 mins. I presumed it was because of the if3 functions in lines 170 and 171 because as soon as I included these lines, the code couldn't be solved.
Even though I defined the days_to_consider variable in lines 61 to 1, I will increase the variable value to 365 days after the code can be executed successfully, which means it is essential for me to solve this code performance problem.
import numpy as np
from gekko import GEKKO
np.set_printoptions(linewidth=2000)
np.set_printoptions(formatter={'float': '{: 0.2f}'.format})
# Given parameters (Please ignore, this is just given input parameters)
left_numeric = np.zeros((14,1))
tempo = [3738.0, 8.5, 1, 1, 1, 2.25, 3.28, 0, 0.6, 1, 0.5, 4, 285000, 0]
for i in range(14):
left_numeric[i,0] = tempo[i]
left_string = ['Urban / City', 'Light : 110,000 * Af', 'Yes', '23. Variable air volume / Water or Water&Air / Air / Yes', '1. Mechanical system only; no provision for natural ventilation', 'Slowly rotating or intermittent heat exchangers (0.7)', 'No exhaust air recirculation', 'Automatic control more than 50%', 'Automatic control more than 50%', 'Taps More Than 3m from Heat Generation', 'Co-Generation (0.9)', '20', '', 'Electricity', 'Electricity', 'Electricity']
weatherData = np.load("Atlanta_TMY3_climate.npy")
weatherData = np.insert(weatherData, 0, np.zeros((1,15)), 0)
Info_Array = np.load("Info_Array.npy")
vsite = 0.8
totalAppliance = 12.0
totalOccupants = 8.39748075577327
hs_unoccupied = 16.0
Cm = 30.55555555
Htr_ms = 22.75
Htr_w = 1.5824055209225467
Htr_is = 15.525
Htr_em = 1.0723552727080168
f_BAC_hc = 0.7
f_BAC_e = 0.87
fcntrl_heat = 0.5
fcntrl_cool = 0.5
dist_heat = 0.9259259
dist_cool = 1.00
totalArea = 877.4
total_DHW_system = 76.40691666666666
effi_gen_DHW = 0.9
occu_equi_hours = 3093.5
Prs = 0.40580206298113436
Prm = 0.5555555555555556
HR_efficiency = 0.7
T_supply_h = 28.0
T_supply_c = 17.0
ventSupply = 2.519244226731981
ventRecirculation = 1
ventilation_cooling_type = 1
dist_heat = 0.9259259
dist_cool = 1.00000
# Create GEKKO model
m = GEKKO(remote=False)
#m.open_folder()
print(m.path)
days_to_consider = 1
m.time = np.linspace(0, 24*days_to_consider, 24*days_to_consider+1)
# Define MVs
fDim = m.MV(lb=1, ub=1, name="fDim") # dummy MV just for testing. In the future more MVs will be added and the interval will also be modified.
fDim.STATUS = 1
f_VT = 1 # this will be an MV, but for now, it is just a constant
# 2D numppy array manipulation (add zero row in the first row of the "Info_Array" and "weatherData")
T_heating_set = m.Param(value = Info_Array[0:24*days_to_consider+1,9], name="T_heating_set")
T_cooling_set = m.Param(value = Info_Array[0:24*days_to_consider+1,10], name="T_cooling_set")
# Define parameters
Te = m.Param(value = weatherData[0:24*days_to_consider+1,0], name="Te")
Wind_speed = m.Param(value = weatherData[0:24*days_to_consider+1,1], name="Wind_speed")
fapp = m.Param(value = Info_Array[0:24*days_to_consider+1,6], name="fapp")
Qapp = totalAppliance
focc = m.Param(value = Info_Array[0:24*days_to_consider+1,5], name="focc")
Qocc = totalOccupants
Qlight = m.Param(value = Info_Array[0:24*days_to_consider+1,8], name="Qlight")
Qsol = m.Param(value = Info_Array[0:24*days_to_consider+1,11], name="Qsol")
qv_infil_wind = m.Param(value = 0.0769*left_numeric[8,0]*(0.75*vsite*weatherData[0:24*days_to_consider+1,1]**2)**0.667, name="qv_infil_wind")
# Define variables
qv_infil_stack = m.Var(0, name="qv_infil_stack")
T_m0_t = m.Var(name="T_m0_t")
T_m10_t = m.Var(name="T_m10_t")
T_m0 = m.Var(name="T_m0")
T_m10 = m.Var(name="T_m10")
T_s0 = m.Var(name="T_s0")
T_s10 = m.Var(name="T_s10")
T_air0 = m.Var(value=hs_unoccupied, name="T_air0")
T_air10 = m.Var(name="T_air10")
T_m_ac_t = m.Var(value=hs_unoccupied, name="T_m_ac_t")
T_m_ac = m.Var(value=hs_unoccupied, name="T_m_ac")
T_s_ac = m.Var(name="T_s_ac")
T_air_ac = m.Var(name="T_air_ac")
T_air_set = m.Var(name="T_air_set")
T_air_set_prev = m.Var(name="T_air_set_prev")
m.delay(T_m_ac_t, T_air_set_prev, 1)
Q_HC_nd = m.Var(value=0, name="Q_HC_nd")
# Define delivered energy
E_heating = m.Var(name="E_heating")
E_cooling = m.Var(name="E_cooling")
E_lighting = m.Var(name="E_lighting")
E_fan = m.Var(name="E_fan")
E_pump = m.Var(name="E_pump")
E_equip = m.Var(name="E_equip")
E_dhw = m.Var(name="E_dhw")
V_heating_sup_air = m.Var(name="V_heating_sup_air")
V_cooling_sup_air = m.Var(name="V_cooling_sup_air")
q_v_sup = m.Var(name="q_v_sup")
# Build building Equations
# 0) Internal heat gains
Qint = m.Intermediate(fapp*Qapp + focc*Qocc + fDim*Qlight, name="Qint")
Qia = m.Intermediate(0.5*Qint, name="Qia")
Qst = m.Intermediate(Prs*(Qia + Qsol), name="Qst")
Qm = m.Intermediate(Prm*(Qia + Qsol), name="Qm")
# 1) Airflow
qv_mech_sup = m.Intermediate(ventSupply*ventRecirculation*focc, name="qv_mech_sup")
m.Equation(qv_infil_stack == 0.0146*left_numeric[8,0]*(0.7*left_numeric[1,0]*m.abs(Te-T_air_set))**0.667)
##qv_infil_stack = m.Intermediate(0.0146*left_numeric[8,0]*(0.7*left_numeric[1,0]*m.abs(Te-T_air_set))**0.667, name="qv_infil_stack")
qv_infil_sw = m.Intermediate(m.max3(qv_infil_stack,qv_infil_wind) + 0.14*qv_infil_stack*qv_infil_wind/left_numeric[8,0], name="qv_infil_sw")
Hve = m.Intermediate((qv_mech_sup*(1-HR_efficiency) + qv_infil_sw)*0.3333, name="Hve")
Htr_1 = m.Intermediate(Hve*Htr_is/(Hve + Htr_is), name="Htr_1")
# 2) Temperature
Tm_denom = m.Intermediate(Htr_ms + Htr_w + Htr_1, name="Tm_denom")
T_m_intermediate = m.Intermediate(0.5*((Htr_1+Htr_w)*Htr_ms/Tm_denom+Htr_em), name="T_m_intermediate")
m.Equations([\
T_m0_t == (1/(Cm+T_m_intermediate))*((Cm-T_m_intermediate)*T_air_set_prev+(Htr_ms*(Htr_w+Htr_1)/Tm_denom+Htr_em)*Te+(Htr_ms/Tm_denom)*Qst+(Htr_ms*Htr_1)/Hve/Tm_denom*(Qia)+Qm),\
T_m10_t == (1/(Cm+T_m_intermediate))*((Cm-T_m_intermediate)*T_air_set_prev+(Htr_ms*(Htr_w+Htr_1)/Tm_denom+Htr_em)*Te+(Htr_ms/Tm_denom)*Qst+(Htr_ms*Htr_1)/Hve/Tm_denom*(Qia+10)+Qm),\
T_m0 == 0.5*(T_air_set_prev+T_m0_t),\
T_m10 == 0.5*(T_air_set_prev+T_m10_t),\
T_s0 == (Htr_ms*T_m0 +(Htr_w+Htr_1)*Te+Qst+Htr_1*Qia/Hve)/Tm_denom,\
T_s10 == (Htr_ms*T_m10+(Htr_w+Htr_1)*Te+Htr_1*10/Hve+Qst+Htr_1*Qia/Hve)/Tm_denom,\
T_air0 == (Htr_is*T_s0 +Hve*Te+Qia)/(Htr_is+Hve),\
T_air10 == (Htr_is*T_s10+Hve*Te+10+Qia)/(Htr_is+Hve)])
# 3. Heating/Cooling need
# Tair set & Q_HC_nd
m.Equations([T_air_set == m.if3(T_air0-T_heating_set, T_heating_set, m.if3(T_cooling_set-T_air0, T_cooling_set, T_air0)),\
Q_HC_nd == 10*(T_air_set-T_air0)/(T_air10-T_air0)])
# 4. Tac & Q_nd
m.Equations([\
T_m_ac_t == (1/(Cm+T_m_intermediate))*((Cm-T_m_intermediate)*T_air_set_prev+(Htr_ms*(Htr_w+Htr_1)/Tm_denom+Htr_em)*Te+(Htr_ms/Tm_denom)*Qst+(Htr_ms*Htr_1)/Hve/Tm_denom*(Qia+Q_HC_nd)+Qm),\
T_m_ac == 0.5*(T_air_set_prev+T_m_ac_t),\
T_s_ac == (Htr_ms*T_m_ac+(Htr_w+Htr_1)*Te+Htr_1*Q_HC_nd/Hve+Qst+Htr_1*Qia/Hve)/Tm_denom,\
T_air_ac == (Htr_is*T_s_ac+Hve*Te+Q_HC_nd+Qia)/(Htr_is+Hve)])
m.Minimize(Q_HC_nd) # please use (uncomment) this line when the upper part of the code (line 1-163) is executed.
#_________________________When below code is included, the solver couldn't solve________________________#
##Q_heat_nd = m.Intermediate(m.if3(Q_HC_nd, 0, Q_HC_nd), name="Q_heat_nd") # suspect that this line slows down the code performance
##Q_cool_nd = m.Intermediate(m.if3(Q_HC_nd, -Q_HC_nd, 0), name="Q_cool_nd")
##
### 5. System delivered energy use (Unit: kW)
##m.Equations([E_heating == Q_heat_nd*f_BAC_hc/(dist_heat*left_numeric[5,0])*totalArea/1000,\
## E_cooling == f_VT*Q_cool_nd*f_BAC_hc/(dist_cool*left_numeric[6,0])*totalArea/1000,\
## E_lighting == fDim*Qlight*totalArea/1000,\
## E_equip == fapp*Qapp*totalArea/1000,\
## E_pump == 8*(fcntrl_heat+fcntrl_cool)/3.6/occu_equi_hours*totalArea,\
## E_dhw == total_DHW_system/effi_gen_DHW/occu_equi_hours*totalArea/1000,\
## V_heating_sup_air == Q_heat_nd*0.0036/(T_supply_h-T_air_ac)/0.001239913,\
## V_cooling_sup_air == Q_cool_nd*0.0036/(T_air_ac-T_supply_c)/0.001239913])
##
##
##if ventilation_cooling_type == 3 and left_numeric[7,0] == 0:
## m.Equation(E_fan == (m.max3(V_heating_sup_air, V_cooling_sup_air))*left_numeric[9,0]*left_numeric[10,0]*f_BAC_e*totalArea/1000)
##
##elif ventilation_cooling_type == 3 and left_numeric[7,0] != 0:
## m.Equation(E_fan == (m.max3(V_heating_sup_air, V_cooling_sup_air)+left_numeric[7,0]*3.6/totalArea*focc)*left_numeric[9,0]*left_numeric[10,0]*f_BAC_e)
##
##elif ventilation_cooling_type != 3 and left_numeric[7,0] == 0:
## m.Equation(E_fan == (m.max3(m.max3(V_heating_sup_air, V_cooling_sup_air),qv_mech_sup*(1-HR_efficiency)))*left_numeric[9,0]*left_numeric[10,0]*f_BAC_e*totalArea/1000)
##
##elif ventilation_cooling_type != 3 and left_numeric[7,0] != 0:
## m.Equation(E_fan == (m.max3(m.max3(V_heating_sup_air, V_cooling_sup_air),qv_mech_sup*(1-HR_efficiency))+left_numeric[7,0]*3.6/totalArea*focc)*left_numeric[9,0]*left_numeric[10,0]*f_BAC_e)
##m.Minimize(E_heating + E_cooling + E_lighting + E_fan + E_equip + E_pump + E_dhw) # please use (uncomment) this line when the entire code is executed.
#_______________________________________________________________________________________________________#
m.options.IMODE = 6
m.options.DIAGLEVEL = 4
m.options.SOLVER = 1
m.options.MAX_ITER = 1000000
m.solve(disp=True, GUI=False, debug=False)
I plan to conduct dynamic optimal control problems, which will be comprised of a total of ~45 MVs from ~10 buildings (the code is shown above, each building has 4-5 MVs) and 1 community PV (1 MV, the model is shown here) for 8,760-time steps(= 365 days x 24 hrs). Do you think this size of the problem is solvable from the IPOPT solver within a few hours on a normal personal laptop (or if the complexity of the IPOPT solver algorithm is available, I would appreciate it)? I think this would be challenging to solve because the number of the MVs becomes about ~400,000 (=~45 MVs x 8,760 hours), and I wonder if the IPOPT solver can handle this size of the optimization problem.
The commented code has 6 max3() functions and 2 if3() functions. Those add one binary variable for each time point so a total of 25 x 8 = 200 binary variables for 1 day and (24*365 + 1) x 8 = 70,088 binary variables for 365 days. The year-long solution would likely take too much time. Here are some suggestions to improve the solution time:
Use m.options.DIAGLEVEL=0 instead of 4. The additional diagnostics take longer with pre-processing.
Try m.options.REDUCE=3 to perform pre-processing to reduce the number of equations, if possible.
Initialize with m.options.SOLVER=3 (IPOPT) and then switch to m.options.SOLVER=1 (APOPT) with to obtain an integer solution. Use m.options.TIME_SHIFT=0 when solving again to keep the initial conditions from changing on the second solve.
Try if2() and max2() as MPCC forms that can solve faster than if3() and max3(), especially for large problems. They sometimes have problems if the solution is at the switch conditions.
Turn off degrees of freedom with .STATUS=0 to first solve without decision variables to obtain an initial feasible solution.
The augmented problem with the lines uncommented appear to be infeasible. Both APOPT and IPOPT reported that the problem is infeasible.
EXIT: Converged to a point of local infeasibility. Problem may be infeasible.
An error occured.
The error code is 2
---------------------------------------------------
Solver : IPOPT (v3.12)
Solution time : 86.0874 sec
Objective : 165.28211287911208
Unsuccessful with error code 0
---------------------------------------------------
The file infeasibilities.txt will have additional insights.
This is a follow-up to my previous question here
I've been trying to convert the color data in a heatmap to RGB values.
source image
In the below image, to the left is a subplot present in panel D of the source image. This has 6 x 6 cells (6 rows and 6 columns). On the right, we see the binarized image, with white color highlighted in the cell that is clicked after running the code below. The input for running the code is the below image. The ouput is(mean = [ 27.72 26.83 144.17])is the mean of BGR color in the cell that is highlighted in white on the right image below.
A really nice solution that was provided as an answer to my previous question is the following (ref)
import cv2
import numpy as np
# print pixel value on click
def mouse_callback(event, x, y, flags, params):
if event == cv2.EVENT_LBUTTONDOWN:
# get specified color
row = y
column = x
color = image[row, column]
print('color = ', color)
# calculate range
thr = 20 # ± color range
up_thr = color + thr
up_thr[up_thr < color] = 255
down_thr = color - thr
down_thr[down_thr > color] = 0
# find points in range
img_thr = cv2.inRange(image, down_thr, up_thr) # accepted range
height, width, _ = image.shape
left_bound = x - (x % round(width/6))
right_bound = left_bound + round(width/6)
up_bound = y - (y % round(height/6))
down_bound = up_bound + round(height/6)
img_rect = np.zeros((height, width), np.uint8) # bounded by rectangle
cv2.rectangle(img_rect, (left_bound, up_bound), (right_bound, down_bound), (255,255,255), -1)
img_thr = cv2.bitwise_and(img_thr, img_rect)
# get points around specified point
img_spec = np.zeros((height, width), np.uint8) # specified mask
last_img_spec = np.copy(img_spec)
img_spec[row, column] = 255
kernel = np.ones((3,3), np.uint8) # dilation structuring element
while cv2.bitwise_xor(img_spec, last_img_spec).any():
last_img_spec = np.copy(img_spec)
img_spec = cv2.dilate(img_spec, kernel)
img_spec = cv2.bitwise_and(img_spec, img_thr)
cv2.imshow('mask', img_spec)
cv2.waitKey(10)
avg = cv2.mean(image, img_spec)[:3]
mean.append(np.around(np.array(avg), 2))
print('mean = ', np.around(np.array(avg), 2))
# print(mean) # appends data to variable mean
if __name__ == '__main__':
mean = [] #np.zeros((6, 6))
# create window and callback
winname = 'img'
cv2.namedWindow(winname)
cv2.setMouseCallback(winname, mouse_callback)
# read & display image
image = cv2.imread('ip2.png', 1)
#image = image[3:62, 2:118] # crop the image to 6x6 cells
#---- resize image--------------------------------------------------
# appended this to the original code
print('Original Dimensions : ', image.shape)
scale_percent = 220 # percent of original size
width = int(image.shape[1] * scale_percent / 100)
height = int(image.shape[0] * scale_percent / 100)
dim = (width, height)
# resize image
image = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
# ----------------------------------------------------------------------
cv2.imshow(winname, image)
cv2.waitKey() # press any key to exit
cv2.destroyAllWindows()
What do I want to do next?
The mean of the RGB values thus obtained has to be mapped to the values in the following legend provided in the source image,
I would like to ask for suggestions on how to map the RGB data to the values in the legend.
Note: In my previous post it has been suggested that one could
fit the RGB values into an equation which gives continuous results.
Any suggestions in this direction will also be helpful.
EDIT:
Answering the comment below
I did the following to measure the RGB values of legend
Input image:
This image has 8 cells in columns width and 1 cell in rows height
Changed these lines of code:
left_bound = x - (x % round(width/8)) # 6 replaced with 8
right_bound = left_bound + round(width/8) # 6 replaced with 8
up_bound = y - (y % round(height/1)) # 6 replaced with 1
down_bound = up_bound + round(height/1) # 6 replaced with 1
Mean obtained for each cell/ each color in legend from left to right:
mean = [ 82.15 174.95 33.66]
mean = [45.55 87.01 17.51]
mean = [8.88 8.61 5.97]
mean = [16.79 17.96 74.46]
mean = [ 35.59 30.53 167.14]
mean = [ 37.9 32.39 233.74]
mean = [120.29 118. 240.34]
mean = [238.33 239.56 248.04]
You can try to apply piece wise approach, make pair wise transitions between colors:
c[i->i+1](t)=t*(R[i+1],G[i+1],B[i+1])+(1-t)*(R[i],G[i],B[i])
Do the same for these values:
val[i->i+1](t)=t*val[i+1]+(1-t)*val[i]
Where i - index of color in legend scale, t - parameter in [0:1] range.
So, you have continuous mapping of 2 values, and just need to find color parameters i and t closest to sample and find value from mapping.
Update:
To find the color parameters you can think about every pair of neighbour legend colors as a pair of 3d points, and your queried color as external 3d point. Now you just meed to find a length of perpendicular from the external point to a line, then, iterating over legend color pairs, find the shortest perpendicular (now you have i).
Then find intersection point of the perpendicular and the line. This point will be located at the distance A from line start and if line length is L then parameter value t=A/L.
Update2:
Simple brutforce solution to illustrate piece wise approach:
#include "opencv2/opencv.hpp"
#include <string>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
Mat Image=cv::Mat::zeros(100,250,CV_32FC3);
std::vector<cv::Scalar> Legend;
Legend.push_back(cv::Scalar(82.15,174.95,33.66));
Legend.push_back(cv::Scalar(45.55, 87.01, 17.51));
Legend.push_back(cv::Scalar(8.88, 8.61, 5.97));
Legend.push_back(cv::Scalar(16.79, 17.96, 74.46));
Legend.push_back(cv::Scalar(35.59, 30.53, 167.14));
Legend.push_back(cv::Scalar(37.9, 32.39, 233.74));
Legend.push_back(cv::Scalar(120.29, 118., 240.34));
Legend.push_back(cv::Scalar(238.33, 239.56, 248.04));
std::vector<float> Values;
Values.push_back(-4);
Values.push_back(-2);
Values.push_back(0);
Values.push_back(2);
Values.push_back(4);
Values.push_back(8);
Values.push_back(16);
Values.push_back(32);
int w = 30;
int h = 10;
for (int i = 0; i < Legend.size(); ++i)
{
cv::rectangle(Image, Rect(i * w, 0, w, h), Legend[i]/255, -1);
}
std::vector<cv::Scalar> Smooth_Legend;
std::vector<float> Smooth_Values;
for (int i = 0; i < Legend.size()-1; ++i)
{
cv::Scalar c1 = Legend[i];
cv::Scalar c2 = Legend[i + 1];
float v1 = Values[i];
float v2 = Values[i+1];
for (int j = 0; j < w; ++j)
{
float t = (float)j / (float)w;
Scalar c = c2 * t + c1 * (1 - t);
float v = v2 * t + v1 * (1 - t);
float x = i * w + j;
line(Image, Point(x, h), Point(x, h + h), c/255, 1);
Smooth_Values.push_back(v);
Smooth_Legend.push_back(c);
}
}
Scalar qp = cv::Scalar(5, 0, 200);
float d_min = FLT_MAX;
int ind = -1;
for (int i = 0; i < Smooth_Legend.size(); ++i)
{
float d = cv::norm(qp- Smooth_Legend[i]);
if (d < d_min)
{
ind = i;
d_min = d;
}
}
std::cout << Smooth_Values[ind] << std::endl;
line(Image, Point(ind, 3 * h), Point(ind, 4 * h), Scalar::all(255), 2);
circle(Image, Point(ind, 4 * h), 3, qp/255,-1);
putText(Image, std::to_string(Smooth_Values[ind]), Point(ind, 70), FONT_HERSHEY_DUPLEX, 1, Scalar(0, 0.5, 0.5), 0.002);
cv::imshow("Legend", Image);
cv::imwrite("result.png", Image*255);
cv::waitKey();
}
The result:
Python:
import cv2
import numpy as np
height=100
width=250
Image = np.zeros((height, width,3), np.float)
legend = np.array([ (82.15,174.95,33.66),
(45.55,87.01,17.51),
(8.88,8.61,5.97),
(16.79,17.96,74.46),
( 35.59,0.53,167.14),
( 37.9,32.39,233.74),
(120.29,118.,240.34),
(238.33,239.56,248.04)], np.float)
values = np.array([-4,-2,0,2,4,8,16,32], np.float)
# width of cell, also defines number
# of one segment transituin subdivisions.
# Larger values will give more accuracy, but will woek slower.
w = 30
# Only fo displaying purpose. Height of bars in result image.
h = 10
# Plot legend cells ( to check correcrness only )
for i in range(len(legend)):
col=legend[i]
cv2.rectangle(Image, (i * w, 0, w, h), col/255, -1)
# Start form smoorhed scales for color and according values
Smooth_Legend=[]
Smooth_Values=[]
for i in range(len(legend)-1): # iterate known knots
c1 = legend[i] # start color point
c2 = legend[i + 1] # end color point
v1 = values[i] # start value
v2 = values[i+1] # emd va;ie
for j in range(w): # slide inside [start:end] interval.
t = float(j) / float(w) # map it to [0:1] interval
c = c2 * t + c1 * (1 - t) # transition between c1 and c2
v = v2 * t + v1 * (1 - t) # transition between v1 and v2
x = i * w + j # global scale coordinate (for drawing)
cv2.line(Image, (x, h), (x, h + h), c/255, 1) # draw one tick of smoothed scale
Smooth_Values.append(v) # append smoothed values for next step
Smooth_Legend.append(c) # append smoothed color for next step
# queried color
qp = np.array([5, 0, 200])
# initial value for minimal distance set to large value
d_min = 1e7
# index for clolor search
ind = -1
# search for minimal distance from queried color to smoothed scale color
for i in range(len(Smooth_Legend)):
# distance
d = cv2.norm(qp-Smooth_Legend[i])
if (d < d_min):
ind = i
d_min = d
# ind contains index of the closest color in smoothed scale
# and now we can extract according value from smoothed values scale
print(Smooth_Values[ind]) # value mapped to queried color.
# plot pointer (to check ourself)
cv2.line(Image, (ind, 3 * h), (ind, 4 * h), (255,255,255), 2);
cv2.circle(Image, (ind, 4 * h), 3, qp/255,-1);
cv2.putText(Image, str(Smooth_Values[ind]), (ind, 70), cv2.FONT_HERSHEY_DUPLEX, 1, (0, 0.5, 0.5), 1);
# show window
cv2.imshow("Legend", Image)
# save to file
cv2.imwrite("result.png", Image*255)
cv2.waitKey()
I am trying to plot a vertical line which represents depth. On different depths different types of soil are present and I would like to indicate these with different colors.
I have received data which is stored in a class. Based on a friction number I want to color the vertical line in three different colors. However, I don't know how I can achieve this.
The data is stored in ds1 (I have many different files (ds2, ds3, .. ds29) so making this work is convenient for me). ds1.depth calls the depth array, ds1.frictionnumber the friction number etc.
Due to the large amount of data and soil changes I don't know how to do this. It would be nice if something like below would work but I cannot get my head around it.
UPDATE: I found some code here -> http://abhay.harpale.net/blog/python/how-to-plot-multicolored-lines-in-matplotlib/
I adjusted my code and I get quite reasonable results, however something still goes wrong. In my data I've got 4 NaN values at the end (friction did not get measured). In the image below this should be from the horizontal line down, however that's not the case. How can I solve this, or what is going wrong?
UPDATE 2: The height of the line apparantly also scales with linewidth. Now I only need to find another way of plotting a wider line. Any suggestions are welcome.
def find_contiguous_colors(colors):
# finds the continuous segments of colors and returns those segments
segs = []
curr_seg = []
prev_color = ''
for c in colors:
if c == prev_color or prev_color == '':
curr_seg.append(c)
else:
segs.append(curr_seg)
curr_seg = []
curr_seg.append(c)
prev_color = c
segs.append(curr_seg) # the final one
return segs
def plot_multicolored_lines(x,y,colors):
segments = find_contiguous_colors(colors)
plt.figure(figsize=(12,8))
start= 0
for seg in segments:
end = start + len(seg)
l, = plt.gca().plot(x[start:end],y[start:end],lw=50,c=seg[0])
start = end
plt.axhline(-5.8988)
plt.axis('equal')
x = np.full(len(ds1.depth), 1)
y = ds1.depth
# color segments
colors = ['red']*len(ds1.depth)
for i in range(len(ds1.depth)):
if 0 < ds1.wrijvingsgetal[i] <= 1:
colors[i] = 'blue'
elif 1 < ds1.wrijvingsgetal[i] <= 2:
colors[i] = 'green'
elif ds1.wrijvingsgetal[i] > 2:
colors[i] = 'yellow'
else:
colors[i] = 'magenta'
Vertical soil profile
It's not efficient at all but I get the results I want. If someone knows how I could fix this by using stacked bargraph that is appreciated (for some reason I can't get it to work). My fix is presented below:
ds1 = read_gef(filepath)
def find_contiguous_colors(colors):
# finds the continuous segments of colors and returns those segments
segs = []
curr_seg = []
prev_color = ''
for c in colors:
if c == prev_color or prev_color == '':
curr_seg.append(c)
else:
segs.append(curr_seg)
curr_seg = []
curr_seg.append(c)
prev_color = c
segs.append(curr_seg)
return segs
def plot_multicolored_lines(x, y, colors):
segments = find_contiguous_colors(colors)
plt.figure(figsize=(12,8))
start= 0
for seg in segments:
end = start + len(seg)
l, = plt.gca().plot(x[start:end], y[start:end], lw=2, c=seg[0])
start = end
#plt.axhline(-5.8988, -1, 1)
x = np.full((len(ds1.depth), 2), 0)
y = np.array(ds1.depth)
# color segments
colors = ['red']*len(ds1.depth)
handle = [0] * 3
for i in range(len(ds1.depth)):
if 0 < ds1.wrijvingsgetal[i] <= 2:
colors[i] = 'yellow'
sandplt = plt.axhline(y[i], color=colors[i])
# elif 1 < ds1.wrijvingsgetal[i] <= 2:
# colors[i] = 'green'
# peatplt = plt.axhline(y[i], color=colors[i])
elif ds1.wrijvingsgetal[i] > 2:
colors[i] = 'green'
clayplt = plt.axhline(y[i], color=colors[i])
else:
colors[i] = 'magenta'
nanplt = plt.axhline(y[i], color=colors[i])
ratioxl = 1/5
plt.xlim(0, ratioxl*(ds1.depth[-1]-ds1.depth[0]))
plt.axis('scaled')
plt.ylabel('Depth w.r.t. groundlevel [m]')
plt.title('Vertical soil profile ' + str(Path(filepath).stem))
plt.tick_params(
axis='x', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom=False, # ticks along the bottom edge are off
top=False, # ticks along the top edge are off
labelbottom=False) # labels along the bottom edge are off
plt.legend(handles = (sandplt, clayplt, nanplt), labels=['Sand', 'Clay', 'Nan'], loc=[1.01, 0.78])
plt.savefig(os.path.join(wd, '..', '03data', '20190711-Wiertsema', 'VN72650-1_GEF', 'afbeeldingen') + '\VSP_' + str(Path(filepath).stem))
plt.show()
I'm working on a project that requires me to select "unique" colors for each item. At times there could be upwards of 400 items. Is there some way out there of selecting the 400 colors that differ the most? Is it as simple as just changing the RGB values by a fixed increment?
You could come up with an equal distribution of 400 colours by incrementing red, green and blue in turn by 34.
That is:
You know you have three colour channels: red, green and blue
You need 400 distinct combinations of R, G and B
So on each channel the number of increments you need is the cube root of 400, i.e. about 7.36
To span the range 0..255 with 7.36 increments, each increment must be about 255/7.36, i.e. about 34
Probably HSL or HSV would be a better representations than RGB for this task.
You may find that changing the hue gives better variability perception to the eye, so adjust your increments in a way that for every X units changed in S and L you change Y (with Y < X) units of hue, and adjust X and Y so you cover the spectrum with your desired amount of samples.
Here is my final code. Hopefully it helps someone down the road.
from PIL import Image, ImageDraw
import math, colorsys, os.path
# number of color circles needed
qty = 400
# the lowest value (V in HSV) can go
vmin = 30
# calculate how much to increment value by
vrange = 100 - vmin
if (qty >= 72):
vdiff = math.floor(vrange / (qty / 72))
else:
vdiff = 0
# set options
sizes = [16, 24, 32]
border_color = '000000'
border_size = 3
# initialize variables
hval = 0
sval = 50
vval = vmin
count = 0
while count < qty:
im = Image.new('RGBA', (100, 100), (0, 0, 0, 0))
draw = ImageDraw.Draw(im)
draw.ellipse((5, 5, 95, 95), fill='#'+border_color)
r, g, b = colorsys.hsv_to_rgb(hval/360.0, sval/100.0, vval/100.0)
r = int(r*255)
g = int(g*255)
b = int(b*255)
draw.ellipse((5+border_size, 5+border_size, 95-border_size, 95-border_size), fill=(r, g, b))
del draw
hexval = '%02x%02x%02x' % (r, g, b)
for size in sizes:
result = im.resize((size, size), Image.ANTIALIAS)
result.save(str(qty)+'/'+hexval+'_'+str(size)+'.png', 'PNG')
if hval + 10 < 360:
hval += 10
else:
if sval == 50:
hval = 0
sval = 100
else:
hval = 0
sval = 50
vval += vdiff
count += 1
Hey I came across this problem a few times in my projects where I wanted to display, say, clusters of points. I found that the best way to go was to use the colormaps from matplotlib (https://matplotlib.org/stable/tutorials/colors/colormaps.html) and
colors = plt.get_cmap("hsv")[np.linspace(0, 1, n_colors)]
this will output rgba colors so you can get the rgb with just
rgb = colors[:,:3]