Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 47 additions & 12 deletions AutoKey.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,15 @@ def empty():
fgdmodel = np.zeros((1,65),np.float64)
cv2.grabCut(img2,mask,rect,bgdmodel,fgdmodel,5,cv2.GC_INIT_WITH_MASK)
elif k == ord('m'):
cv2.imwrite('tmp.pbm',bwimg)
# Write 1 channel
cv2.imwrite('tmp.pbm',bwimg[:, :, 1])
subprocess.check_call([
"potrace",
"tmp.pbm",
"--tight", "-s",
"-o", "tmp.svg"
])
subprocess.check_call(["inkscape", "-h", str(img.shape[0]), "-b", "white", "-e", "tmp.png", "tmp.svg"])
subprocess.check_call(["inkscape", "-h", str(img.shape[0]), "-b", "white", "--export-type", "png", "--export-filename", "tmp.png", "tmp.svg"])
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently newer inkscape uses --export-filename while the older (Ubuntu LTS) does not support that. We likely need some logic to determine which switches to use. I can implement that later. That applies for all the inkscape invocations.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using inkscape from Kali, so maybe another version (Inkscape v1.2.2 today), but the web version fix that point :)

svg = cv2.imread("tmp.png")

mh = img.shape[0] - svg.shape[0]
Expand All @@ -241,7 +242,7 @@ def empty():
if mw <= 0:
mw = 0

svg = cv2.copyMakeBorder(svg, mh/2, mh/2 + mh % 2, mw/2, mw/2 + mw % 2, cv2.BORDER_CONSTANT, value=(255, 255, 255, 255))
svg = cv2.copyMakeBorder(svg, int(mh/2), int(mh/2 + mh % 2), int(mw/2), int(mw/2 + mw % 2), cv2.BORDER_CONSTANT, value=(255, 255, 255, 255))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did this cause any errors previously? would be good to know :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think yes, but to be honest I did some update some months ago and maybe I had an error.
If you are waiting for int only (not float), you can keep it in case, or you can remove it and see if it's working for you.


mask2 = np.where((mask==1) + (mask==3),255,0).astype('uint8')
output = cv2.bitwise_and(img2,img2,mask=mask2)
Expand All @@ -261,6 +262,15 @@ def empty():


cv2.destroyAllWindows()
# Remove tmp file, that should be moved in another location...
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found the tempfiles actually helpful in case something crashed. But maybe we can put this behind a flag. I will think about it :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, an option should be great, or even a folder, you can choose what do you want ;)

try:
os.remove("tmp.pbm")
os.remove("tmp.png")
os.remove("tmp.svg")
os.remove("grabcut_output.png")
os.remove("grabcut_summary.png")
except OSError:
pass
return

def main(argv=None):
Expand Down Expand Up @@ -293,6 +303,10 @@ def main(argv=None):
parser.add_argument("--thin-handle", dest="thin_handle", action='store_true', required=False, help="Use a thin handle suitable for impressioning grips")
parser.add_argument("--match-handle-connector", dest="match_handle_connector", action='store_true', required=False, help="Make the handle same thickness as the connector")

# Outname
parser.add_argument("--refinname", dest="refinname", action='store_true', required=False, help="Add the profile/description/keys in filename like key-AB95-E20-12345.stl")
parser.add_argument("--output", dest="output", required=False, help="Set the output name", metavar="FILE")

parser.add_argument('args', nargs=argparse.REMAINDER)

if len(argv) == 0:
Expand Down Expand Up @@ -359,14 +373,14 @@ def main(argv=None):

# Look for length in system definition for branding
for line in definition.splitlines():
m = re.match("\s*kl\s*=\s*([\d\.]+)\s*;", line)
m = re.match(r"\s*kl\s*=\s*([\d\.]+)\s*;", line)
if m:
def_kl = m.group(1)
next

# Look for tolerance in profile definition for branding
for idx,line in enumerate(profile_definition.splitlines()):
m = re.match("\s*tol\s*=\s*([\d\.]+)\s*;", line)
m = re.match(r"\s*tol\s*=\s*([\d\.]+)\s*;", line)
if m:
def_tol = m.group(1)
def_tol_idx = idx
Expand All @@ -389,13 +403,22 @@ def main(argv=None):
branding = branding.replace("%model%", model)
branding = branding.replace("%length%", "%s" % def_kl)
branding = branding.replace("%tol%", "%s" % def_tol)
if opts.bumpkey:
branding = branding.replace("%code%", "%s" % "bumpkey")
elif opts.blank:
branding = branding.replace("%code%", "%s" % "blank")
elif opts.key:
branding = branding.replace("%code%", "%s" % (opts.key if opts.key else "NA"))
else:
branding = branding.replace("%code%", "%s" % "NA")

with open(os.path.join(BRAND_DIR, "branding.svg"), 'w') as f:
f.write(branding)

DEVNULL = open(os.devnull, 'w')

subprocess.check_call(["inkscape", "--export-eps", os.path.join(BRAND_DIR, "branding.eps"), os.path.join(BRAND_DIR, "branding.svg"),])
subprocess.check_call(["pstoedit", "-nb", "-dt", "-f", "dxf:-polyaslines", os.path.join(BRAND_DIR, "branding.eps"), os.path.join(BRAND_DIR, "branding.dxf")], stderr=DEVNULL)
subprocess.check_call(["inkscape", "--export-type", "eps","--export-filename", os.path.join(BRAND_DIR, "branding.eps"), os.path.join(BRAND_DIR, "branding.svg"),])
subprocess.check_call(["pstoedit", "-nb", "-dt", "-f", "dxf:-polyaslines", os.path.join(BRAND_DIR, "branding.eps"), os.path.join(BRAND_DIR, "branding.dxf")], stderr=subprocess.DEVNULL)

# Read base settings
with open(os.path.join(BASE_DIR, "base-settings.scad"), 'r') as f:
Expand Down Expand Up @@ -434,7 +457,7 @@ def main(argv=None):
combination = opts.key.split(",")
for idx in range(0, len(combination)):
try:
int(combination[idx])
float(combination[idx])
except ValueError:
combination[idx] = '"%s"' % combination[idx]
f.write("combination = [%s];\n" % ",".join(combination))
Expand Down Expand Up @@ -462,10 +485,22 @@ def main(argv=None):
f.write("include <includes/default-keycombcuts.scad>;")
f.write("\n")

subprocess.check_call(["inkscape", "--export-eps", os.path.join(BASE_DIR, "profile.eps"), opts.profile])
subprocess.check_call(["pstoedit", "-nb", "-dt", "-f", "dxf:-polyaslines", os.path.join(BASE_DIR, "profile.eps"), os.path.join(BASE_DIR, "profile.dxf")], stderr=DEVNULL)
subprocess.check_call(["openscad", os.path.join(BASE_DIR, "key.scad") ])

outputname = "key.stl"
if opts.refinname:
# result
code = "NA"
if opts.bumpkey:
code = "bumbkey"
if opts.blank:
code = "blank"
if opts.key:
code = "".join(opts.key.split(","))
outputname = "key_{}_{}_{}_{}_{}.stl".format(os.path.basename(opts.profile).replace(".svg", ""), model, code, def_kl, def_tol)
elif opts.output:
outputname = opts.output
subprocess.check_call(["inkscape", "--export-type", "eps","--export-filename", os.path.join(BASE_DIR, "profile.eps"), opts.profile])
subprocess.check_call(["pstoedit", "-nb", "-dt", "-f", "dxf:-polyaslines", os.path.join(BASE_DIR, "profile.eps"), os.path.join(BASE_DIR, "profile.dxf")], stderr=subprocess.DEVNULL)
subprocess.check_call(["/usr/bin/openscad", os.path.join(BASE_DIR, "key.scad") ])

if __name__ == "__main__":
sys.exit(main())
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@
AutoKey3D (formerly known as PhotoBump) is a software to create 3D models for
key blanks, bumpkeys and regular keys.

This is an updated fork with the addition of metric measures.
For that, some things should be updated on files before the key generation.
1. You should have the distance between the shoulder and the first PIN for the **aspace** variable on the metric.scad file
2. You shoud have the key length for the **kl** variable on the metric.scad file
3. You should have the space between each pin for the variable **pinspace** on the metric.scad file
3. You should have the height of each PIN from the key base
4. You can now generate the key by using the command like this `python3 AutoKey.py --key 6.7,6.3,7.8,6.6,6.7 --profile profiles/metric.svg --definition definitions/metric.scad`
5. You should to update the metric.scg file by te profile that you ant to use.

Note: The original author of the tool created a web site with that tool but the metric is not still implemented. A [pull request](https://github.com/choller/autokey3d/pull/7) is open to add that feature to avoid having all the dependencies installed on a computer (https://autokey.own-hero.net)

## License

Please note that AutoKey3D is released under a *non-commercial* license (CC BY-NC-SA 4.0).
Expand Down
10 changes: 7 additions & 3 deletions branding/branding-template.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions definitions/metric.scad
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// This definition is also valid for: AB A90

// Key length
kl=27.7;

// Combination cuts

// Shoulder
aspace = 4.7;

// Pin distance
pinspace = 4.05;


// not used
// Highest cut
hcut = ph - 2*tol - 4.45;

// Cut spacing
cutspace = 1; // Actually alternating between 0.43/0.44

// Cut angle
cutangle = 112;

// Plateau spacing of the cut
platspace = 0.0;

// cur the key from the bottom by giving the size in milimeter.
realdim = true;
16 changes: 8 additions & 8 deletions includes/default-keycombcuts.scad
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
include <regularcut.scad>;

module keycombcuts(laser=false) {
module keycombcuts(laser=false, realdim=false) {
for (i = [0:len(keycomb)-1]) {
keycombcut(i, keycomb[i], aspace, laser);
keycombcut(i, keycomb[i], aspace, laser, realdim=realdim);
}
if (laser) {
keycombcuts_laser();
keycombcuts_laser(realdim=realdim);
}
}

module keycombcuts_laser() {
module keycombcuts_laser(realdim=false) {
for (i = [0:len(keycomb)-1]) {
if (i < len(keycomb)-1) {
hull() {
keycombcut(i, keycomb[i], aspace, true, false, true);
keycombcut(i+1, keycomb[i+1], aspace, true, true, false);
keycombcut(i, keycomb[i], aspace, true, false, true, realdim=realdim);
keycombcut(i+1, keycomb[i+1], aspace, true, true, false, realdim=realdim);
}
}
}
hull() {
keycombcut(len(keycomb)-1, keycomb[len(keycomb)-1], aspace, true, false, true);
keycombcut(len(keycomb)-1, keycomb[len(keycomb)-1], aspace, true, false, true, realdim=realdim);
translate([0,0,-kl])
keycombcut(len(keycomb)-1, keycomb[len(keycomb)-1], aspace, true, false, true);
keycombcut(len(keycomb)-1, keycomb[len(keycomb)-1], aspace, true, false, true, realdim=realdim);
}
}
7 changes: 5 additions & 2 deletions includes/regularcut.scad
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module keycombcut(cutnum, cutlevel, loc_aspace, lcut_mode=false, lcut1=false, lcut2=false, rot=0, xcorr=0) {
module keycombcut(cutnum, cutlevel, loc_aspace, lcut_mode=false, lcut1=false, lcut2=false, rot=0, xcorr=0, realdim=false) {
// echo(cutnum, cutlevel, loc_aspace, lcut_mode, lcut1, lcut2, rot, xcorr);
lcut = lcut1 || lcut2;
cutdim = 10;
d = cutdim / sqrt(2); // Diagonal of the cutting rect
Expand All @@ -19,7 +20,9 @@ module keycombcut(cutnum, cutlevel, loc_aspace, lcut_mode=false, lcut1=false, lc
ycorrect = d * sin(rotangle2) / cos(rotangle2/2) * sin(45+rotangle2/2);
zcorrect = d * sin(-rotangle2) / cos(-rotangle2/2) * sin(45-rotangle2/2);

translate([0, addcutdepth + hcut-(cutlevel*cutspace + cutcorr + lcutcorr), 0])
cutsize = realdim ? (ph - cutlevel) : (addcutdepth + hcut-(cutlevel*cutspace + cutcorr + lcutcorr));

translate([0, cutsize, 0])
translate([0,0,(loc_aspace + addaspace + cutnum*pinspace)*-1]) // Pin position
translate([0,-cutdim/2 + tol, -cutdim/2 + kl/2]) // Center the cutter at 0. We need to add the tolerance to reach the lower end of the thinned key.
//translate([0,-cutdim/2, -cutdim/2 + kl/2]) // Center the cutter at 0. We need to add the tolerance to reach the lower end of the thinned key.
Expand Down
2 changes: 1 addition & 1 deletion key.scad
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ union() {
keytipcuts();

if (!blank) {
keycombcuts(lasercut);
keycombcuts(lasercut, realdim);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume your goal here was to allow the combination to be specified in mm rather than code? I would recommend doing this by specifying a system that has a cutspace resolution that is a 0.1mm (there is nothing under that precision happening usually with keys) and then just multiplying your values by 10 and going off the highest possible cut.

I will think about adding this, however I am currently refactoring the combination system anyway, using 1 as the shallowest cut like most key programs do, so the codes stay comparable. It could take a while for me to merge things.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, what profile is the svg (i.e. what lock/brand did it belong to?)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That point was the main point for my PR in fact.
My goal was to create a key from a picture with the help of the KeyDecoder tool : https://play.google.com/store/apps/details?id=com.keydecoder

So, I would like to have the possibility to enter the measure from the botom of the key in mm.
So yes 0.1mm should be enougth.

So if you are refactoring all the tool, if you can think regarding this issue and adding a profile named "mm" or something like that it could be good.

As I'm not a key expert, I'm not able to say "this key with this profile possess these definitions", I'm juste able to get the tooth length in mm, the space between the shoulder and the first pin, and le length of the key. From that, I Would be able to create the same key.

Regarding the SVG, I don't know which profile is it. In the same way, I would like to create the profile from a picture and the key from a picture (with the measure from the KeyDecoder application) without knowing the brand, type, ... so from a "key noob" point of view ^^

I hope it was clear :)

}
}
khcyo = -(khcy-ph)/2;
Expand Down
6 changes: 6 additions & 0 deletions profiles/metric.scad
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// The tolerance to use when removing material from the profile
tol = 0.2;

// Key profile height (including tolerance, i.e. measured on the lock, not the blank)
// If you have information on the key blank height, add 2*tol.
ph=8.8 + 2*tol;
61 changes: 61 additions & 0 deletions profiles/metric.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.