Hackster is hosting Hackster Holidays, Ep. 5: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 5 on Friday!
Michael Portera
Published © GPL3+

Trading Card Scanner/Organizer

Create a digital inventory of your trading/collectible cards using Lego and a Raspberry Pi.

IntermediateFull instructions provided4 hours30,763
Trading Card Scanner/Organizer

Things used in this project

Hardware components

Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Breadboard (generic)
Breadboard (generic)
×1
Capacitor 100 µF
Capacitor 100 µF
Optional
×1
SparkFun Breadboard Power Supply 5V/3.3V
SparkFun Breadboard Power Supply 5V/3.3V
×1
Lego Medium Creative Brick Box
×1
Camera Module
Raspberry Pi Camera Module
×1
Servo 360
https://www.amazon.com/gp/product/B019TOJPO4/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1
×1

Software apps and online services

AWS Rekognition
Amazon Web Services AWS Rekognition
AWS S3
Amazon Web Services AWS S3

Story

Read more

Schematics

Close Up of Breadboard

Code

MTG_Servo

Python
This is the script we run in order to scan our cards. Simply provide the set abbreviation as an argument e.g. python mtg_servo.py jou (jou for Journey Into Nyx). We can simply stop the script once we've run out of cards.
from PIL import Image
from picamera import PiCamera
import RPi.GPIO as GPIO
import time
import sys

servoPin=18
miniServoPin=24
camera = PiCamera()
set_name = sys.argv[1]

def camera_setup(camera):
	camera.color_effects = (128,128)
	camera.rotation = 90
	camera.resolution = (300,100)


def setup():
	GPIO.setwarnings(False)
	GPIO.setmode(GPIO.BCM)
	GPIO.setup(servoPin,GPIO.OUT)
	GPIO.setup(miniServoPin,GPIO.OUT)

setup()
camera_setup(camera)

servo=GPIO.PWM(servoPin,50)
miniServo=GPIO.PWM(miniServoPin,50)
miniServo.start(2.5)
time.sleep(1)

while True:
	servo.start(2.5)
	time.sleep(1.5)
	servo.stop()
	timestamp=time.strftime("%Y%m%d%H%M%S")
	camera.capture(set_name+"/"+set_name+"_"+timestamp+".jpg")
	print "Picture Taken! See {0}_{1}.jpg!".format(set_name,timestamp)
	miniServo.ChangeDutyCycle(7.5)
	time.sleep(1)
	miniServo.ChangeDutyCycle(2.5)

GPIO.cleanup()

Rekognition Analysis and TCGplayer Price Lookup

Python
This should be run AFTER we've uploaded our cards to our S3 bucket. The script is looking for a file path containing the images we took, mine being organized by set. It creates an array of the .jpg names and then queries the AWS bucket to get the DetectedText. Next, the code will query TCGplayer.com's pricing API using the DetectedText from Rekognition.
import requests
import sys
import os
import time
import boto3

#Define our global variables
#This argument is defined when we run our script. This tells us which MTG set/expansion we are looking up
card_set = sys.argv[1]

#The name of the s3 bucket we are requesting
BUCKET = "YOUR_BUCKET_NAME"

#Initialize an empty array to store the images for us to lookup
card_list = []

#TCGplayer.com variables
headers = {'Accept': 'application/json','Authorization':'bearer YOUR_API_CREDENTIALS'}
product_url = "http://api.tcgplayer.com/catalog/products"
group_id_url = "http://api.tcgplayer.com/catalog/categories/1/groups" #Magic the Gathering Category ID is 1

#Open a file for us to write the jpeg file name, detected text from Rekognition, and the market price from TCG
oFile=open('mtg_prices.txt','a')

def get_group_id(card_set):
	for k in range(0,2):
		off = 100 * k
		querystring = {"offset": off,"limit":"100"}
		response = requests.request("GET", group_id_url, headers=headers, params=querystring)
		data = response.json()
		for i in data['results']:
			#productId=i['name']
			#productName=i['abbreviation']
			if card_set == str(i['abbreviation']).lower():
			#print "{0},{1},{2}".format(groupId,productId,productName)
				groupId = i['groupId']
				break		
	return groupId
	

#Create a function that will take the 3 letter card abbreviation and look for the folder in our current directory. Then store the file name into the card_list array
def get_cards(card_set):
	for i in os.listdir("/YOUR/FILE/PATH/WITH/PICTURES/"+card_set):
		card_list.append(i)
	return card_list

#Create a function that will lookup the jpeg filename in s3 to be processed by Rekognition
def detect_labels(bucket, key, region="us-east-1"):
	rekognition = boto3.client("rekognition", region)
	response = rekognition.detect_text(
		Image={
			"S3Object": {
				"Bucket": bucket,
				"Name": key,
			}
		},
		
	)
	return response['TextDetections']

#Lookup the price our card as detected by Rekognition
def tcg_lookup(card_picture, card_name, card_set, groupId):
	querystring = {"categoryId":"1","productName":card_name,"limit":"10"}
	response = requests.get(product_url, headers=headers, params=querystring)
	data = response.json()
	if data['success']!=True:
		print "Sorry I don't know that one. Writing to file..." #If the text doesn't match a card, we'll write it to file anyways so we can submit it later
		oFile.write("{0},{1},{2}\n".format(card_picture, card_set, card_name))
	else:
		for j in data['results']:
			 if str(j['groupId']) == str(groupId):
				productId = j['productId']
				price_url = "http://api.tcgplayer.com/pricing/product/{0}".format(productId)
				response = requests.get(price_url,headers=headers)
				data = response.json()
				print "The market price of {0} is {1}".format(card_name,data['results'][0]['marketPrice'])
				oFile.write("{0},{1},{2}\n".format(card_picture,card_set,card_name,productId,data['results'][0]['marketPrice']))
	

get_cards(card_set)
print "Added {0} pictures to lookup...".format(str(len(card_list)))
groupId = get_group_id(card_set)
print "GroupId for {0} is {1}".format(card_set,groupId)

for i in card_list:
	KEY="{0}/{1}".format(card_set,i)
	rek_response = detect_labels(BUCKET,KEY)
	ftext = str(rek_response[0]['DetectedText'])
	print "Detected text: {0}. Looking up card".format(ftext)
	tcg_lookup(i,ftext,card_set,groupId)
	time.sleep(1)

Credits

Michael Portera

Michael Portera

4 projects • 15 followers

Comments