How to Read Nonstandard 1D Barcodes with Dynamsoft Barcode SDK in Python
Start and stop characters are defined by 1D (linear) barcode standards. Sometimes you’ll encounter nonstandard barcodes where these characters differ. This guide takes Code 39 as the example and shows how to decode such barcodes in Python with the Dynamsoft Barcode Reader (via the Dynamsoft Capture Vision Bundle) by supplying a custom template.
Prerequisites
- Python 3.x
- A free trial license for Dynamsoft Barcode Reader SDK
-
Install the SDK:
pip install dynamsoft-capture-vision-bundle
Dataset (for comparison)
Standard Code 39 (start/stop *)

Nonstandard Code 39 (start/stop +)

Nonstandard Code 39 (start/stop -)

Quick Start: Read a Standard Code 39
Here is the code snippet for decoding a standard 1D barcode image:
from dynamsoft_capture_vision_bundle import *
license_key = "LICENSE-KEY" # https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&package=cross-platform
cvr_instance = CaptureVisionRouter()
error_code, error_message = LicenseManager.init_license(license_key)
result = cvr_instance.capture(filename, EnumPresetTemplate.PT_READ_BARCODES.value)
if result.get_error_code() != EnumErrorCode.EC_OK:
print("Error:", result.get_error_code(),
result.get_error_string())
else:
items = result.get_items()
print('Found {} barcodes.'.format(len(items)))
for item in items:
format_type = item.get_format_string()
text = item.get_text()
print("Barcode Format:", format_type)
print("Barcode Text:", text)
location = item.get_location()
x1 = location.points[0].x
y1 = location.points[0].y
x2 = location.points[1].x
y2 = location.points[1].y
x3 = location.points[2].x
y3 = location.points[2].y
x4 = location.points[3].x
y4 = location.points[3].y
print("Location Points:")
print("({}, {})".format(x1, y1))
print("({}, {})".format(x2, y2))
print("({}, {})".format(x3, y3))
print("({}, {})".format(x4, y4))
print("-------------------------------------------------")
Reading Nonstandard 1D Barcodes with Custom Templates
To decode variants (e.g., Code 39 using + or - as start/stop), provide a custom template that tells the SDK what to expect.
Minimal template for Code 39 with + start/stop
Save as template_plus.json:
{
"BarcodeFormatSpecificationOptions": [
{
"BarcodeFormatIds": [
"BF_DEFAULT",
"BF_NONSTANDARD_BARCODE"
],
"HeadModuleRatio": "131113131",
"Name": "bfs_0",
"StandardFormat": "BF_CODE_39",
"TailModuleRatio": "131113131"
}
],
"BarcodeReaderTaskSettingOptions": [
{
"BarcodeFormatIds": [
"BF_DEFAULT",
"BF_NONSTANDARD_BARCODE"
],
"BarcodeFormatSpecificationNameArray": [
"bfs_0"
],
"Name": "dbr_task_0",
"SectionArray": [
{
"ImageParameterName": "IP_0",
"Section": "ST_REGION_PREDETECTION",
"StageArray": [
{
"Stage": "SST_PREDETECT_REGIONS"
}
]
},
{
"ImageParameterName": "IP_0",
"Section": "ST_BARCODE_LOCALIZATION",
"StageArray": [
{
"Stage": "SST_LOCALIZE_CANDIDATE_BARCODES"
},
{
"Stage": "SST_LOCALIZE_BARCODES"
}
]
},
{
"ImageParameterName": "IP_0",
"Section": "ST_BARCODE_DECODING",
"StageArray": [
{
"Stage": "SST_RESIST_DEFORMATION"
},
{
"Stage": "SST_COMPLEMENT_BARCODE"
},
{
"Stage": "SST_SCALE_BARCODE_IMAGE"
},
{
"Stage": "SST_DECODE_BARCODES"
}
]
}
]
}
],
"CaptureVisionTemplates": [
{
"ImageROIProcessingNameArray": [
"roi_default"
],
"Name": "CV_0",
"Timeout": 1000000
}
],
"ImageParameterOptions": [
{
"ApplicableStages": [
{
"Stage": "SST_INPUT_COLOR_IMAGE"
},
{
"Stage": "SST_SCALE_IMAGE"
},
{
"Stage": "SST_CONVERT_TO_GRAYSCALE"
},
{
"Stage": "SST_TRANSFORM_GRAYSCALE"
},
{
"Stage": "SST_ENHANCE_GRAYSCALE"
},
{
"Stage": "SST_BINARIZE_IMAGE"
},
{
"Stage": "SST_DETECT_TEXTURE"
},
{
"Stage": "SST_REMOVE_TEXTURE_FROM_GRAYSCALE"
},
{
"Stage": "SST_BINARIZE_TEXTURE_REMOVED_GRAYSCALE"
},
{
"Stage": "SST_FIND_CONTOURS"
},
{
"Stage": "SST_DETECT_SHORTLINES"
},
{
"Stage": "SST_ASSEMBLE_LINES"
},
{
"Stage": "SST_DETECT_TEXT_ZONES"
},
{
"Stage": "SST_REMOVE_TEXT_ZONES_FROM_BINARY"
}
],
"Name": "IP_0"
}
],
"TargetROIDefOptions": [
{
"Name": "roi_default",
"TaskSettingNameArray": [
"dbr_task_0"
]
}
]
}
Minimal template for Code 39 with - start/stop
Save as template_minus.json. Only the tail/head ratios differ:
{
"BarcodeFormatSpecificationOptions": [
{
"BarcodeFormatIds": [
"BF_DEFAULT",
"BF_NONSTANDARD_BARCODE"
],
"HeadModuleRatio": "131111313",
"Name": "bfs_0",
"StandardFormat": "BF_CODE_39",
"TailModuleRatio": "131111313"
}
],
"BarcodeReaderTaskSettingOptions": [
{
"BarcodeFormatIds": [
"BF_DEFAULT",
"BF_NONSTANDARD_BARCODE"
],
"BarcodeFormatSpecificationNameArray": [
"bfs_0"
],
"Name": "dbr_task_0",
"SectionArray": [
{
"ImageParameterName": "IP_0",
"Section": "ST_REGION_PREDETECTION",
"StageArray": [
{
"Stage": "SST_PREDETECT_REGIONS"
}
]
},
{
"ImageParameterName": "IP_0",
"Section": "ST_BARCODE_LOCALIZATION",
"StageArray": [
{
"Stage": "SST_LOCALIZE_CANDIDATE_BARCODES"
},
{
"Stage": "SST_LOCALIZE_BARCODES"
}
]
},
{
"ImageParameterName": "IP_0",
"Section": "ST_BARCODE_DECODING",
"StageArray": [
{
"Stage": "SST_RESIST_DEFORMATION"
},
{
"Stage": "SST_COMPLEMENT_BARCODE"
},
{
"Stage": "SST_SCALE_BARCODE_IMAGE"
},
{
"Stage": "SST_DECODE_BARCODES"
}
]
}
]
}
],
"CaptureVisionTemplates": [
{
"ImageROIProcessingNameArray": [
"roi_default"
],
"Name": "CV_0",
"Timeout": 1000000
}
],
"ImageParameterOptions": [
{
"ApplicableStages": [
{
"Stage": "SST_INPUT_COLOR_IMAGE"
},
{
"Stage": "SST_SCALE_IMAGE"
},
{
"Stage": "SST_CONVERT_TO_GRAYSCALE"
},
{
"Stage": "SST_TRANSFORM_GRAYSCALE"
},
{
"Stage": "SST_ENHANCE_GRAYSCALE"
},
{
"Stage": "SST_BINARIZE_IMAGE"
},
{
"Stage": "SST_DETECT_TEXTURE"
},
{
"Stage": "SST_REMOVE_TEXTURE_FROM_GRAYSCALE"
},
{
"Stage": "SST_BINARIZE_TEXTURE_REMOVED_GRAYSCALE"
},
{
"Stage": "SST_FIND_CONTOURS"
},
{
"Stage": "SST_DETECT_SHORTLINES"
},
{
"Stage": "SST_ASSEMBLE_LINES"
},
{
"Stage": "SST_DETECT_TEXT_ZONES"
},
{
"Stage": "SST_REMOVE_TEXT_ZONES_FROM_BINARY"
}
],
"Name": "IP_0"
}
],
"TargetROIDefOptions": [
{
"Name": "roi_default",
"TaskSettingNameArray": [
"dbr_task_0"
]
}
]
}
Notes
StandardFormatselects the base symbology (BF_CODE_39).-
HeadModuleRatioandTailModuleRatiodefine the start and stop characters. From the Code 39 character table:131113131 → +, 131111313 → -.
Now modify the Python code to read the nonstandard 1D barcodes.
from dynamsoft_capture_vision_bundle import *
json_file = None
if special_character == '+':
json_file = r"template_plus.json"
if special_character == '-':
json_file = r"template_minus.json"
if json_file == None:
return
license_key = "LICENSE-KEY"
cvr_instance = CaptureVisionRouter()
error_code, error_message = LicenseManager.init_license(license_key)
cvr_instance.init_settings_from_file(json_file)
result = cvr_instance.capture(filename, "") # The empty string means using the default template
if result.get_error_code() != EnumErrorCode.EC_OK:
print("Error:", result.get_error_code(),
result.get_error_string())
else:
items = result.get_items()
print('Found {} barcodes.'.format(len(items)))
for item in items:
format_type = item.get_format_string()
text = item.get_text()
print("Barcode Format:", format_type)
print("Barcode Text:", text)
location = item.get_location()
x1 = location.points[0].x
y1 = location.points[0].y
x2 = location.points[1].x
y2 = location.points[1].y
x3 = location.points[2].x
y3 = location.points[2].y
x4 = location.points[3].x
y4 = location.points[3].y
print("Location Points:")
print("({}, {})".format(x1, y1))
print("({}, {})".format(x2, y2))
print("({}, {})".format(x3, y3))
print("({}, {})".format(x4, y4))
print("-------------------------------------------------")
