Lossless sRGB <-> Lab conversion? - colors

When I convert black (rgb(0,0,0)) to LAB and back again it comes out as 19, 0, 10.
Is there a modified formula that expands the Lab color-space for 1:1 conversion? And if not, are there any other color-spaces that maintain the property of "the same amount of numerical change in these values corresponds to roughly the same amount of visually perceived change"?
I want to run k-means clustering on some images and it works better in Lab space.

The short answer is the code library you are using has bugs.
And it does not seem to be actively maintained.
FWIW, when you are looking at values, remember that:
#000
sRGB 0,0,0 = linearRGB 0.0,0.0,0.0 = XYZ 0,0,0 = Lab 0,0,0
(All spaces 0,0,0)
#FFF
sRGB 255,255,255 = linearRGB 1.0,1.0,1.0 = XYZ 0.9505,1.0,1.0888 = Lab 100,0,0
(sRGB is D65, this assumes D65 2° observer)
#777
sRGB 119,119,119 = linearRGB 0.1845,0.1845,0.1845 = XYZ 0.1753,0.1845,0.2009 = Lab 50.03,0,0
(using BruceLindbloom Matrix)
Pseudocode
More correct code can be found here:
https://www.easyrgb.com/en/math.php
However, this code is NOT javascript — the code on this site is pseudocode, and so it needs to be modified (i.e. you need to use Math.pow and not ^ )
Python
There is a good Python based library here, and it is actively maintained:
https://github.com/colour-science/colour
MATH
The actual math and discussion can be found on Bruce Lindbloom's site:
http://www.brucelindbloom.com/index.html?Math.html
He also has some color calculators that are JS that might be helpful to you.
Happy to answer other questions, lemme know...

Related

Calculate CV2 Homography by points and line

I have a list of points in the field (like upper_goal_point/ left_upper_outer_corner, etc.
I know their corresponding coordinates in destination image - so I can calculate the homography:
h, status = cv2.findHomography(pts_src, pts_dst)
I also have blue points in the upper corner line (look at image above), which I only know that their destination's y coordinates are 0 (because they are in the upper line), but I don't know where exactly they lay in that line.
Can I use those blue points in order to improve the homography?
P.S.
You can see that the upper corner line in the homography is not horizontal line, it's diagonal, which of course is not correct:
Actually it possible to use line correspondence in find homography.
https://www.researchgate.net/publication/220845575_Combining_Line_and_Point_Correspondences_for_Homography_Estimation
Several years ago we implement this approach in one project. During simplification all math we come up with simple trick. We transform every line a*x + b*y + c = 0 to point (a/c, b/c)
// *** Don't copy paste this code, read below! ***//
Point2f convertPointsToHomogeneousLine(Point2f p1, Point2f p2) {
Point3f p1h(p1.x, p1.y, 1);
Point3f p2h(p2.x, p2.y, 1);
Point3f lineHomo(p1h.y*p2h.z - p1h.z*p2h.y,
p1h.z*p2h.x - p1h.x*p2h.z,
p1h.x*p2h.y - p1h.y*p2h.x);
Point2f lineHomoNorm(lineHomo.x / lineHomo.z,
lineHomo.y / lineHomo.z);
return lineHomoNorm;
}
And pass this points inside. As I remember I also dig inside OpenCV implementation of findHomography and insert this lines somewhere inside to solve step. Inside OpenCV there some normalization applied to points before pass to solve step. So you need to skip this step for this kind of points.
We do not use it in production. User need to calibrate camera manually by providing lines and points on the image and in meter system. It has too complicated interface and bad stability. But in your case I think it can work better. If you will automatically find lines and correspondence.
P.S. Please note that in paper they use some normalization technique.
It will improve stability. We faced with stability problem, do not
solved it in our journey.

How to solve misconversion when formatting fantasy currency in Microsoft Excel?

I am trying to set up Excel for my D&D Fantasy Currency.
I want to use Excel to convert integers and decimals to gold (gp), silver (sp), and copper (cp) pieces; e.g. 25.47 = 25gp 4sp 7cp.
The conversion is fairly simple, as seen on this conversion table or below.
1sp = 10cp
1gp = 10sp = 100cp
I got this code from an answer that user4039065 posted on as similar question asked by Juddson Ivines - How to format fantasy currency in Microsoft Excel?:
=TRIM(TEXT(INT(J6),"0 \g\p ;;;")&TEXT(--RIGHT(TEXT(J6,"0.0")),"0 \s\p ;;;")&TEXT(--RIGHT(TEXT(J6,"0.00")),"0 \c\p ;;;"))
This code at first glance worked amazingly.
However when it came to converting 0.6 to 6cp, the code messes up and answers as 1cp 6sp. For some reason the code is adding 1sp.
A further error occurs at 0.95-0.99, where it drops the 9sp entirely.
I even gridded out from 1cp to over 2gp to check for errors, which you can see here, which is how I caught the 0.95 error.
Thanks to user4039065 for their answer and starting code, as well as Juddson Ivines for asking the original question.
Apologies for being new here and my lack of being able to embed my images.
I would really appreciate some help with correcting the format of this code.
Formula in column C is:
=TRIM(IF(B5>=1;INT(B5)&"gp ";"")&IF(B5-INT(B5)>=0,1;MID(B5-INT(B5);3;1)&"sp ";"")&IF(LEN(B5-INT(B5))>3;VALUE(MID(B5-INT(B5);4;2))&"cp";""))
You may benefit from several functions to achieve this. The tricky part is to get an IF for each type of coin and also avoid left zeros in the copper coins (that's the VALUE duty)
Probably sometimes you may get weird output due to decimals. You just need to Round the values.

.translated function not working in Godot / best way to change position of bone

I'm working on a program in Godot using Gdscript, and I ran into a problem when trying to use the Transform.translated(Vector3) function. My code is supposed to move a bone to (0,0,0) by translating it by its current coordinates but with negative sign. Example: (1,2,3) would be translated by (-1,-2,-3) so it would end up at (0,0,0). For some reason when I do this, the end position of the bone is not (0,0,0), but some other coordinate. In the Godot documents, it says the .translated function is "relative to the transform's basis vectors", so maybe that's why? Also if there is a better way to change a bones position than using the Transform.translated(Vector3) function that would be helpful too. Thanks!
My Code:
bonePose = skel.get_bone_global_pose(bone)
var globalBonePose = skel.to_global(bonePose.origin)
translateVector = -globalBonePose
var newPose = bonePose.translated(translateVector)
skel.set_bone_pose(bone, newPose)
Code Output / Results:
bonePose (the original position of the bone) is around (-0.82,0.49,0.50)
translateVector (the amount the bone will be translated) is around (0.82,-0.49,-0.50)
newPose (the final position of the bone -- should be [0,0,0]) is around (0.82,-0.66,-0.46). Even when I call skel.to_global(newPose.origin) to see the global coordinates, it's (-0.76,0.44,0.42), which is not (0,0,0)
In Godot a Transform is composed of a basis (a Basis) and an origin (a Vector3). Where the origin handles the translation part of the transform, and the Basis the rest.
A Basis is the set of vectors that define the coordinate system. There is a vector that defines the x axis, another for the y axis, and another for the z axis. And this is the way Godot will encode rotation and scaling transformations.
When the documentation says "relative to the transform's basis vectors" it means the Basis will be applied to the vector you pass in. Thus, in your case, you are getting a translation on the local space of the bone. Which implies that if the bone is rotated or scaled (or something like that), that will affect the translation.
If you don't want to deal with rotation, scaling, et.al. I suggest you work with the origin of the Transform instead.
If you have a Transform and you want another that is otherwise equal but located at (0, 0, 0), you do this:
var new_transform = Transform(transform.basis, Vector.ZERO)
Or replace Vector.ZERO with whatever origin you want to give the new transform.
I also need to remind you that get_bone_global_pose and set_bone_pose do not operate on the same thing. On one hand set_bone_pose is relative to the parent bone, on the other get_bone_global_pose is relative to the Skeleton. Thus, I suggest you use set_bone_global_pose_override instead.
The final piece you need is the opposite of Spatial.to_global. Because setting the pose like as follows…
bonePose = skel.get_bone_global_pose(bone)
var newPose = Transform(bonePose.basis, Vector.ZERO)
skel.set_bone_global_pose_override(bone, newPose, 1.0)
… Would place it at the origin of the Skeleton.
Well, the opposite of Spatial.to_global is Spatial.to_local, and you would use it like this:
bonePose = skel.get_bone_global_pose(bone)
var newPose = Transform(bonePose.basis, skel.to_local(Vector.ZERO))
skel.set_bone_global_pose_override(bone, newPose, 1.0)
Here skel.to_local(Vector.ZERO) should give the origin of the world relative to the Skeleton. And given that set_bone_global_pose_override wants a Transform relative to the Skeleton, the result should be that the bone is placed at the origin of the world. With its rotation and scaling preserved.

Given two colors, both in HSV(B), how can I convert one to another using LESS CSS?

I'm designing some colored blocks, where the text is one color and the background is a lighter / desaturated shade of that same color (think warning text, where the words is red and it has a light pinkish background).
The text color is #990000. The background color is #f2dede. In Photoshop HSV, that equates to hsv( 0, 100%, 60%) & hsv(0, 8%, 95%).
With this color set, I'd like to be able to toss in other colors -- a nice green or yellow -- and have the background automatically calculated.
Using LESSCSS, I've tried:
#color: desaturate( lighten( hsv(0, 100%, 60%), 35%), 92% );
But I can't get it to render the color I generate by performing the same changes in Photoshop.
The color theory of this is beyond my comprehension, but my question is: How do I convert one color to another color, given the Photoshop HSB values?
Update:
I get it a little better -- the L and the B in HSB and HSL are not at all compatible, so lightening the color the "B" delta is never going to work.
It's sloppy, but something like this works:
#foreground: #990000;
#background: hsv(hsvhue(#foreground),
(hsvsaturation(#foreground)-92%),
(hsvvalue(#foreground)+35%));
Lacking a "value" command similar to lighten/darken, I'm thinking this is the best I'm going to get. (If I don't get any other responses, I'll mark this the answer).
Thanks much...Nate
Note: my answer here discusses some issues with color theory and LESS functions that you may find useful also.
For LESS 1.4 (which has some new built in functions)
LESS
#color: hsv(0, 100%, 60%);
#bkg: hsv(hsvhue(#color), (hsvsaturation(#color) - 92%), (hsvvalue(#color) + 34.75%));
.test {
color: #color;
background-color: #bkg;
}
CSS Output
.test {
color: #990000;
background-color: #f2dede;
}
The link I posted above may explain why my value is 34.75% and not 35% more like your numbers were expecting.
UPDATE: I just noticed that you had essentially posted a similar answer in your question. I'm not certain exactly what issue you are still facing with that method of calculation.
UPDATE 2: If you desire it to generally always "contrast" then something like this may work better:
#color: hsv(0, 100%, 60%);
#darkerBkg: hsv(hsvhue(#color), (hsvsaturation(#color) - 92%), (hsvvalue(#color) + 34.75%));
#lighterBkg: hsv(hsvhue(#color), (hsvsaturation(#color) + 92%), (hsvvalue(#color) - 34.75%));
#bkg: contrast(#color, #darkerBkg, #lighterBkg, 43%);
.test {
color: #color;
background-color: #bkg;
}
Using the contrast function with the default 43% threshold value will cause a "flip" to the #lighterBkg in this case at a 35% saturation value (hsv(0,35%,60%)).
The calculation numbers you have set are going to work best with colors in the initial saturation range of 92-100 (or with the contrast feature also 0-8), and initial value range of 0-65 (or with contrast you would probably be covered in any value range). In the contrast version, any saturation between 9-91 will begin to "equalize" to either 0% or 100%. You would need to decide on a new "calculation" and add some other math logic in if that was an issue, but I think with the contrast version it may not be a problem.
Give this tool a try:
Cuttle - Color function suggestions based on example
https://www.ofcodeandcolor.com/cuttle/
It takes two different input colors and calculates (or approximates) different ways of going from one color to the other using Less or Sass color functions. Created by #alex-gyoshev.
You'd have to convert your hsv values to rgb first, whilst taking into account the tipps from ScottS about the different color spaces.

How to light-up a sprite in cocos2d?

I've already know how to dark-down a CCSprite object by:
sprite.color = ccc3(x, x, x); // x is a value less then 255
(As far as i know, it should be a direct mapping of openGL functions, so its easy to achieve.)
But when it comes to light-up, my current solution is adding another mask sprite (same shape but all in white), changing its blendFunc to { GL_SRC_ALPHA, GL_ONE } and overlaying it onto the target. Besides all the codes added, there should be a mask image for each need-to-light-up one.
Is there a way to do light-up as easily as dark-down?
However, not as easy as setColor, in Cocos2d 2.x, with OpenGL ES 2.0 support, you can achieve this by using custom shaders. You can get started here:
http://www.raywenderlich.com/10862/how-to-create-cool-effects-with-custom-shaders-in-opengl-es-2-0-and-cocos2d-2-x
You may also try inverting the sprite's darker color to get a lighter one.

Resources