0

I have an image A with shadow appearance in it and I have an image B which is of solid color. I want to copy shadows of image A to Image B from OpenCV in python. If I am not wrong it has something to do with gradient of the image?

karlphillip
  • 92,053
  • 36
  • 243
  • 426
  • Welcome to StackOverflow. This site does not serve as general issue solving portal. The community will help you to get a working solution, but you should share your code showing what you have done already that did not work. Read "How to create a Minimal, Reproducible Example" (https://stackoverflow.com/help/minimal-reproducible-example), "What are good topics" (https://stackoverflow.com/help/on-topic) and "How do I ask a good question" (https://stackoverflow.com/help/how-to-ask)? – fmw42 Dec 27 '19 at 20:12

1 Answers1

1

You can do that easily with ImageMagick or Python Wand or with a bit more effort in Python/OpenCV.

Note that Python Wand uses ImageMagick.

ImageMagick is already available on most Linux distributions and is available for Windows and Mac OSX.

The process is to convert image B to grayscale and then composite it with image A using the hard light compose method.

Image A:

Image B:

With ImageMagick (Unix syntax), the code would be:

convert skirt_A.png \
\( skirt_B.png -colorspace gray -level 25x100% \) \
-compose hardlight -composite skirt_A_shaded.png

Image A Shaded from Image B:

Change the 0.25 in the level operator to make the shading darker or lighter.

Using Python Wand the code would be:

from wand.image import Image
from wand.display import display

with Image(filename='skirt_A.png') as bimg:
    with Image(filename='skirt_B.png') as fimg:
        fimg.transform_colorspace('gray')
        fimg.level(black=0.25,white=1,channel='all_channels')
        bimg.composite_channel('all_channels', fimg, 'hard_light', 0, 0)
        bimg.save(filename='skirt_A_shaded.png')
        display(bimg)

Image A Shaded from Image B:

With Python/OpenCV, the code would be:

import cv2
import numpy as np

# read image_A and convert to float in range 0 to 1
image_A = cv2.imread('skirt_A.png').astype("float32") / 255.0

# read image_B as grayscale and convert to float in range 0 to 1
image_B = cv2.imread('skirt_B.png',0).astype("float32") / 255.0

# convert image_B from grayscale to 3 equal channels as rgb so that the image multiplication in the hard light compositing will work properly
image_B = cv2.cvtColor(image_B,cv2.COLOR_GRAY2RGB) 

# apply linear transform to stretch image_B to make shading darker
# y = A*x+B
# x=1 -> y=1; x=0.25 -> y=0
# 1 = A + B
# 0 = 0.25*A + B
# Solve simultaneous equations to get:
# A = 1.33
# B = -0.33
image_B = 1.33 * image_B -0.33

# threshold image_B and invert
thresh = cv2.threshold(image_B,0.5,1,cv2.THRESH_BINARY)[1]
thresh_inv = 1-thresh

# do hard light composite and convert to uint8 in range 0 to 255
# see CSS specs at https://www.w3.org/TR/compositing-1/#blendinghardlight
low = 2.0 * image_A * image_B
high = 1 - 2.0 * (1-image_A) * (1-image_B)
result = ( 255 * (low * thresh_inv + high * thresh) ).clip(0, 255).astype(np.uint8)

# show results
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

# save results
cv2.imwrite('skirt_A_shaded.png', result)

Image A Shaded from Image B:

karlphillip
  • 92,053
  • 36
  • 243
  • 426
fmw42
  • 46,825
  • 10
  • 62
  • 80
  • How do we decide the values of x and y? You have chosen it (1,1) and (0,.25) in linear transformation. @fmw42 – Saurabh Agrawal Dec 28 '19 at 05:04
  • Just adjust it as needed and look at your results. Find a value that works reasonably for you. I tried varying it up from 0 by 0.05 increments until it looked about like your image_B. All you need to do is change the 0.25 to some other value and do the simultaneous solutions to get the A and B constants. You can also just write the equation to make use of the value 0.25 or other values and have Python compute the A and B constants. Solve `y=A*x+B with 1 = A + B (so that A = 1-B) and 0 = A*c + B. The solution is B=-c/(1-c) and A=1-B=1+c/(1-c) where c is between 0 and 1 and I had used c=0.25` – fmw42 Dec 28 '19 at 06:56
  • Thanks @fmw42. It's working perfectly. But it distorts color of the solid image a little. I guess that I have to tune according to coefficients i.e A and B. – Saurabh Agrawal Dec 28 '19 at 13:28
  • The issue is that the image is modified everywhere by the modified image_B in grayscale form where ever it is not exactly 50% gray. Further adjustment would be needed to make most of it 50% gray and the shadows only darker. This is not easy. You can try to adjust the A and B constants and that might help. Try use 0.20 rather than 0.25 to make the results a bit lighter. Adjust as desired. – fmw42 Dec 28 '19 at 23:01